From 3bef7bbb389f7b048983bac8dda32af0e662a9fb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 07:59:52 -0600 Subject: [PATCH 01/76] Get all the projects created and hooked up to the sln --- OpenConsole.sln | 105 +++++++ .../LocalTests_Remoting.def | 3 + .../Remoting.LocalTests.vcxproj | 78 +++++ .../LocalTests_Remoting/RemotingTests.cpp | 295 ++++++++++++++++++ src/cascadia/LocalTests_Remoting/pch.cpp | 4 + src/cascadia/LocalTests_Remoting/pch.h | 40 +++ .../Microsoft.Terminal.RemotingLib.vcxproj | 87 ++++++ .../Remoting/Resources/en-US/Resources.resw | 120 +++++++ .../dll/Microsoft.Terminal.Remoting.def | 3 + .../dll/Microsoft.Terminal.Remoting.vcxproj | 74 +++++ src/cascadia/Remoting/packages.config | 4 + src/cascadia/Remoting/pch.cpp | 4 + src/cascadia/Remoting/pch.h | 48 +++ .../WindowsTerminal/WindowsTerminal.vcxproj | 5 +- 14 files changed, 868 insertions(+), 2 deletions(-) create mode 100644 src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def create mode 100644 src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj create mode 100644 src/cascadia/LocalTests_Remoting/RemotingTests.cpp create mode 100644 src/cascadia/LocalTests_Remoting/pch.cpp create mode 100644 src/cascadia/LocalTests_Remoting/pch.h create mode 100644 src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj create mode 100644 src/cascadia/Remoting/Resources/en-US/Resources.resw create mode 100644 src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def create mode 100644 src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj create mode 100644 src/cascadia/Remoting/packages.config create mode 100644 src/cascadia/Remoting/pch.cpp create mode 100644 src/cascadia/Remoting/pch.h diff --git a/OpenConsole.sln b/OpenConsole.sln index 364216a7cbf..2d5c1deb4f5 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -323,6 +323,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} EndProjectSection EndProject + + Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} @@ -341,6 +343,25 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} EndProjectSection EndProject + + +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting.Lib", "src\cascadia\Remoting\Microsoft.Terminal.RemotingLib.vcxproj", "{43ce4ce5-0010-4b99-9569-672670d26e26}" +EndProject + +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting", "src\cascadia\Remoting\dll\Microsoft.Terminal.Remoting.vcxproj", "{27b5aaeb-a548-44cf-9777-f8baa32af7ae}" + ProjectSection(ProjectDependencies) = postProject + {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + EndProjectSection +EndProject + +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68a10cd3-aa64-465b-af5f-ed4e9700543c}" + ProjectSection(ProjectDependencies) = postProject + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {27b5aaeb-a548-44cf-9777-f8baa32af7ae} + {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + EndProjectSection +EndProject + + Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2137,6 +2158,87 @@ Global {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.Build.0 = Release|x64 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.Build.0 = Release|Win32 + + + + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x64.ActiveCfg = Release|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.Build.0 = AuditMode|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.Build.0 = Debug|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.ActiveCfg = Debug|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.Build.0 = Debug|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.ActiveCfg = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.Build.0 = Debug|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|Any CPU.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.ActiveCfg = Release|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.Build.0 = Release|ARM64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.ActiveCfg = Release|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.Build.0 = Release|x64 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.ActiveCfg = Release|Win32 + {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.Build.0 = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x64.ActiveCfg = Release|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.Build.0 = AuditMode|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.Build.0 = Debug|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.ActiveCfg = Debug|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.Build.0 = Debug|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.ActiveCfg = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.Build.0 = Debug|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|Any CPU.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.ActiveCfg = Release|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.Build.0 = Release|ARM64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.ActiveCfg = Release|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.Build.0 = Release|x64 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.ActiveCfg = Release|Win32 + {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.Build.0 = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.Build.0 = AuditMode|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.Build.0 = Debug|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.ActiveCfg = Debug|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.Build.0 = Debug|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.ActiveCfg = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.Build.0 = Debug|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|Any CPU.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.ActiveCfg = Release|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.Build.0 = Release|ARM64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.ActiveCfg = Release|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.Build.0 = Release|x64 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.ActiveCfg = Release|Win32 + {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2221,6 +2323,9 @@ Global {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {43ce4ce5-0010-4b99-9569-672670d26e26} = {59840756-302F-44DF-AA47-441A9D673202} + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {59840756-302F-44DF-AA47-441A9D673202} + {68a10cd3-aa64-465b-af5f-ed4e9700543c} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def b/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def new file mode 100644 index 00000000000..8c1a02932d0 --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj b/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj new file mode 100644 index 00000000000..c91a5d003e8 --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj @@ -0,0 +1,78 @@ + + + + + + + {68a10cd3-aa64-465b-af5f-ed4e9700543c} + Win32Proj + RemotingLocalTests + LocalTests_Remoting + Remoting.LocalTests + DynamicLibrary + 10.0.18362.0 + 10.0.18362.0 + true + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + ..;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\jsoncpp\json;$(OpenConsoleDir)src\inc;$(OpenConsoleDir)src\inc\test;$(WinRT_IncludePath)\..\cppwinrt\winrt;"$(OpenConsoleDir)\src\cascadia\Remoting\Generated Files";%(AdditionalIncludeDirectories) + pch.h + + + 4702;%(DisableSpecificWarnings) + + + onecoreuap.lib;%(AdditionalDependencies) + + + + + true + true + + + + + + + + diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp new file mode 100644 index 00000000000..60491de9cbf --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp @@ -0,0 +1,295 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "../TerminalSettingsModel/ColorScheme.h" +#include "../TerminalSettingsModel/CascadiaSettings.h" +#include "JsonTestClass.h" +#include "TestUtils.h" +#include +#include "../ut_app/TestDynamicProfileGenerator.h" + +using namespace Microsoft::Console; +using namespace WEX::Logging; +using namespace WEX::TestExecution; +using namespace WEX::Common; +using namespace winrt::Microsoft::Terminal::Settings::Model; +using namespace winrt::Microsoft::Terminal::TerminalControl; + +namespace SettingsModelLocalTests +{ + // TODO:microsoft/terminal#3838: + // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for + // an updated TAEF that will let us install framework packages when the test + // package is deployed. Until then, these tests won't deploy in CI. + + class SerializationTests : public JsonTestClass + { + // Use a custom AppxManifest to ensure that we can activate winrt types + // from our test. This property will tell taef to manually use this as + // the AppxManifest for this test class. + // This does not yet work for anything XAML-y. See TabTests.cpp for more + // details on that. + BEGIN_TEST_CLASS(SerializationTests) + TEST_CLASS_PROPERTY(L"RunAs", L"UAP") + TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") + END_TEST_CLASS() + + TEST_METHOD(GlobalSettings); + TEST_METHOD(Profile); + TEST_METHOD(ColorScheme); + TEST_METHOD(CascadiaSettings); + + TEST_CLASS_SETUP(ClassSetup) + { + InitializeJsonReader(); + InitializeJsonWriter(); + return true; + } + + private: + // Method Description: + // - deserializes and reserializes a json string representing a settings object model of type T + // - verifies that the generated json string matches the provided one + // Template Types: + // - : The type of Settings Model object to generate (must be impl type) + // Arguments: + // - jsonString - JSON string we're performing the test on + // Return Value: + // - the JsonObject representing this instance + template + void RoundtripTest(const std::string& jsonString) + { + const auto json{ VerifyParseSucceeded(jsonString) }; + const auto settings{ T::FromJson(json) }; + const auto result{ settings->ToJson() }; + + // Compare toString(json) instead of jsonString here. + // The toString writes the json out alphabetically. + // This trick allows jsonString to _not_ have to be + // written alphabetically. + VERIFY_ARE_EQUAL(toString(json), toString(result)); + } + }; + + void SerializationTests::GlobalSettings() + { + const std::string globalsString{ R"( + { + "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", + + "initialRows": 30, + "initialCols": 120, + "initialPosition": ",", + "launchMode": "default", + "alwaysOnTop": false, + + "copyOnSelect": false, + "copyFormatting": "all", + "wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502", + + "alwaysShowTabs": true, + "showTabsInTitlebar": true, + "showTerminalTitleInTitlebar": true, + "tabWidthMode": "equal", + "tabSwitcherMode": "mru", + + "startOnUserLogin": false, + "theme": "system", + "snapToGridOnResize": true, + "disableAnimations": false, + + "confirmCloseAllTabs": true, + "largePasteWarning": true, + "multiLinePasteWarning": true, + + "experimental.input.forceVT": false, + "experimental.rendering.forceFullRepaint": false, + "experimental.rendering.software": false + })" }; + + const std::string smallGlobalsString{ R"( + { + "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" + })" }; + + RoundtripTest(globalsString); + RoundtripTest(smallGlobalsString); + } + + void SerializationTests::Profile() + { + const std::string profileString{ R"( + { + "name": "Windows PowerShell", + "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", + + "commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "startingDirectory": "%USERPROFILE%", + + "icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png", + "hidden": false, + + "tabTitle": "Cool Tab", + "suppressApplicationTitle": false, + + "fontFace": "Cascadia Mono", + "fontSize": 12, + "fontWeight": "normal", + "padding": "8, 8, 8, 8", + "antialiasingMode": "grayscale", + + "cursorShape": "bar", + "cursorColor": "#CCBBAA", + "cursorHeight": 10, + + "altGrAliasing": true, + + "colorScheme": "Campbell", + "tabColor": "#0C0C0C", + "foreground": "#AABBCC", + "background": "#BBCCAA", + "selectionBackground": "#CCAABB", + + "useAcrylic": false, + "acrylicOpacity": 0.5, + + "backgroundImage": "made_you_look.jpeg", + "backgroundImageStretchMode": "uniformToFill", + "backgroundImageAlignment": "center", + "backgroundImageOpacity": 1.0, + + "scrollbarState": "visible", + "snapOnInput": true, + "historySize": 9001, + + "closeOnExit": "graceful", + "experimental.retroTerminalEffect": false + })" }; + + const std::string smallProfileString{ R"( + { + "name": "Custom Profile" + })" }; + + // Setting "tabColor" to null tests two things: + // - null should count as an explicit user-set value, not falling back to the parent's value + // - null should be acceptable even though we're working with colors + const std::string weirdProfileString{ R"( + { + "name": "Weird Profile", + "tabColor": null, + "foreground": null, + "source": "local" + })" }; + + RoundtripTest(profileString); + RoundtripTest(smallProfileString); + RoundtripTest(weirdProfileString); + } + + void SerializationTests::ColorScheme() + { + const std::string schemeString{ R"({ + "name": "Campbell", + + "cursorColor": "#FFFFFF", + "selectionBackground": "#131313", + + "background": "#0C0C0C", + "foreground": "#F2F2F2", + + "black": "#0C0C0C", + "blue": "#0037DA", + "cyan": "#3A96DD", + "green": "#13A10E", + "purple": "#881798", + "red": "#C50F1F", + "white": "#CCCCCC", + "yellow": "#C19C00", + "brightBlack": "#767676", + "brightBlue": "#3B78FF", + "brightCyan": "#61D6D6", + "brightGreen": "#16C60C", + "brightPurple": "#B4009E", + "brightRed": "#E74856", + "brightWhite": "#F2F2F2", + "brightYellow": "#F9F1A5" + })" }; + + RoundtripTest(schemeString); + } + + void SerializationTests::CascadiaSettings() + { + const std::string settingsString{ R"({ + "$schema": "https://aka.ms/terminal-profiles-schema", + "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", + + "profiles": { + "defaults": { + "fontFace": "Zamora Code" + }, + "list": [ + { + "fontFace": "Cascadia Code", + "guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", + "name": "HowettShell" + }, + { + "hidden": true, + "name": "BhojwaniShell" + }, + { + "antialiasingMode": "aliased", + "name": "NiksaShell" + } + ] + }, + "schemes": [ + { + "name": "Cinnamon Roll", + + "cursorColor": "#FFFFFD", + "selectionBackground": "#FFFFFF", + + "background": "#3C0315", + "foreground": "#FFFFFD", + + "black": "#282A2E", + "blue": "#0170C5", + "cyan": "#3F8D83", + "green": "#76AB23", + "purple": "#7D498F", + "red": "#BD0940", + "white": "#FFFFFD", + "yellow": "#E0DE48", + "brightBlack": "#676E7A", + "brightBlue": "#5C98C5", + "brightCyan": "#8ABEB7", + "brightGreen": "#B5D680", + "brightPurple": "#AC79BB", + "brightRed": "#BD6D85", + "brightWhite": "#FFFFFD", + "brightYellow": "#FFFD76" + } + ], + "actions": [ + {"command": { "action": "renameTab","input": "Liang Tab" },"keys": "ctrl+t" } + ], + "keybindings": [ + { "command": { "action": "sendInput","input": "VT Griese Mode" },"keys": "ctrl+k" } + ] + })" }; + + auto settings{ winrt::make_self(false) }; + settings->_ParseJsonString(settingsString, false); + settings->_ApplyDefaultsFromUserSettings(); + settings->LayerJson(settings->_userSettings); + settings->_ValidateSettings(); + + const auto result{ settings->ToJson() }; + VERIFY_ARE_EQUAL(toString(settings->_userSettings), toString(result)); + } +} diff --git a/src/cascadia/LocalTests_Remoting/pch.cpp b/src/cascadia/LocalTests_Remoting/pch.cpp new file mode 100644 index 00000000000..3c27d44d570 --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/src/cascadia/LocalTests_Remoting/pch.h b/src/cascadia/LocalTests_Remoting/pch.h new file mode 100644 index 00000000000..a88cde20a9c --- /dev/null +++ b/src/cascadia/LocalTests_Remoting/pch.h @@ -0,0 +1,40 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ + +#pragma once + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#define BLOCK_TIL +// This includes support libraries from the CRT, STL, WIL, and GSL +#include "LibraryIncludes.h" +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#include +#include +#include + +#include +#include +#include "consoletaeftemplates.hpp" + +#include +#include +#include +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" + +// Common includes for most tests: +#include "../../inc/argb.h" +#include "../../inc/conattrs.hpp" +#include "../../types/inc/utils.hpp" +#include "../../inc/DefaultSettings.h" diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj new file mode 100644 index 00000000000..bc034434b95 --- /dev/null +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -0,0 +1,87 @@ + + + + {43ce4ce5-0010-4b99-9569-672670d26e26} + Win32Proj + Microsoft.Terminal.Remoting.Lib + Microsoft.Terminal.Remoting + Microsoft.Terminal.Remoting.Lib + 10.0.17763.0 + StaticLibrary + Console + true + + + + + + + + Monarch.idl + + + + + Peasant.idl + + + + + + Monarch.idl + + + Create + + + Peasant.idl + + + + + + + + + + + + + + + + + + + + {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} + false + + + + + + + pch.h + ..;$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); + + 4702;%(DisableSpecificWarnings) + + + WindowsApp.lib;user32.lib;shell32.lib;%(AdditionalDependencies) + + + false + + + + + + + diff --git a/src/cascadia/Remoting/Resources/en-US/Resources.resw b/src/cascadia/Remoting/Resources/en-US/Resources.resw new file mode 100644 index 00000000000..f4af46df557 --- /dev/null +++ b/src/cascadia/Remoting/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/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def new file mode 100644 index 00000000000..8c1a02932d0 --- /dev/null +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj new file mode 100644 index 00000000000..e5077043835 --- /dev/null +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -0,0 +1,74 @@ + + + + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} + Microsoft.Terminal.Remoting + Microsoft.Terminal.Remoting + + + DynamicLibrary + Console + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {18D09A24-8240-42D6-8CB6-236EEE820263} + + + + true + true + + + + + + + $(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); + + + User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) + + /INCLUDE:_DllMain@12 + /INCLUDE:DllMain + + + + false + + + + diff --git a/src/cascadia/Remoting/packages.config b/src/cascadia/Remoting/packages.config new file mode 100644 index 00000000000..8a013cf32b2 --- /dev/null +++ b/src/cascadia/Remoting/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/src/cascadia/Remoting/pch.cpp b/src/cascadia/Remoting/pch.cpp new file mode 100644 index 00000000000..3c27d44d570 --- /dev/null +++ b/src/cascadia/Remoting/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/src/cascadia/Remoting/pch.h b/src/cascadia/Remoting/pch.h new file mode 100644 index 00000000000..165aefe02d8 --- /dev/null +++ b/src/cascadia/Remoting/pch.h @@ -0,0 +1,48 @@ +// 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 + +// Including TraceLogging essentials for the binary +#include +#include +TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider); +#include +#include + +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index c66d6c80333..aa45f987aba 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -77,6 +77,7 @@ + @@ -98,7 +99,7 @@ - + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. @@ -106,7 +107,7 @@ - + From 1f52d3583390e300b8253d605b5f79adac5e921b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 08:08:55 -0600 Subject: [PATCH 02/76] Yank all the M/P files, this builds?! --- src/cascadia/Remoting/Monarch.cpp | 186 ++++++++++++++++++ src/cascadia/Remoting/Monarch.h | 55 ++++++ src/cascadia/Remoting/Monarch.idl | 12 ++ src/cascadia/Remoting/MonarchFactory.h | 49 +++++ src/cascadia/Remoting/Peasant.cpp | 51 +++++ src/cascadia/Remoting/Peasant.h | 31 +++ src/cascadia/Remoting/Peasant.idl | 18 ++ .../dll/Microsoft.Terminal.Remoting.vcxproj | 2 +- 8 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 src/cascadia/Remoting/Monarch.cpp create mode 100644 src/cascadia/Remoting/Monarch.h create mode 100644 src/cascadia/Remoting/Monarch.idl create mode 100644 src/cascadia/Remoting/MonarchFactory.h create mode 100644 src/cascadia/Remoting/Peasant.cpp create mode 100644 src/cascadia/Remoting/Peasant.h create mode 100644 src/cascadia/Remoting/Peasant.idl diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp new file mode 100644 index 00000000000..6a74171004f --- /dev/null +++ b/src/cascadia/Remoting/Monarch.cpp @@ -0,0 +1,186 @@ +#include "pch.h" +#include "Monarch.h" + +#include "Monarch.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + Monarch::Monarch() + { + printf("Instantiated a Monarch\n"); + } + + Monarch::~Monarch() + { + printf("~Monarch()\n"); + } + + uint64_t Monarch::GetPID() + { + return GetCurrentProcessId(); + } + + uint64_t Monarch::AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant) + { + // TODO: This whole algorithm is terrible. There's gotta be a better way + // of finding the first opening in a non-consecutive map of int->object + auto providedID = peasant.GetID(); + + if (providedID == 0) + { + peasant.AssignID(_nextPeasantID++); + printf("Assigned the peasant the ID %lld\n", peasant.GetID()); + } + else + { + printf("Peasant already had an ID, %lld\n", peasant.GetID()); + _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; + } + auto newPeasantsId = peasant.GetID(); + _peasants[newPeasantsId] = peasant; + _setMostRecentPeasant(newPeasantsId); + printf("(the next new peasant will get the ID %lld)\n", _nextPeasantID); + + peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + + return newPeasantsId; + } + + void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& /*args*/) + { + if (auto peasant{ sender.try_as() }) + { + auto theirID = peasant.GetID(); + _setMostRecentPeasant(theirID); + } + } + + winrt::Microsoft::Terminal::Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) + { + auto peasantSearch = _peasants.find(peasantID); + return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + } + + void Monarch::_setMostRecentPeasant(const uint64_t peasantID) + { + _mostRecentPeasant = peasantID; + printf("\x1b[90mThe most recent peasant is now \x1b[m#%llu\n", _mostRecentPeasant); + } + + void Monarch::SetSelfID(const uint64_t selfID) + { + this->_thisPeasantID = selfID; + // Right now, the monarch assumes the role of the most recent + // window. If the monarch dies, and a new monarch takes over, then the + // entire stack of MRU windows will go with it. That's not what you + // want! + // + // In the real app, we'll have each window also track the timestamp it + // was activated at, and the monarch will cache these. So a new monarch + // could re-query these last activated timestamps, and reconstruct the + // MRU stack. + // + // This is a sample though, and we're not too worried about complete + // correctness here. + _setMostRecentPeasant(_thisPeasantID); + } + + bool Monarch::ProposeCommandline(array_view args, winrt::hstring cwd) + { + auto argsProcessed = 0; + std::wstring fullCmdline; + for (const auto& arg : args) + { + fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + fullCmdline += L" "; + } + wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); + wprintf(fullCmdline.c_str()); + wprintf(L"\"\n"); + + bool createNewWindow = true; + + if (args.size() >= 3) + { + // We'll need three args at least - [WindowsTerminal.exe, -s, + // id] to be able to have a session ID passed on the commandline. + + if (args[1] == L"-s" || args[1] == L"--session") + { + auto sessionId = std::stoi({ args[2].data(), args[2].size() }); + printf("Found a commandline intended for session %d\n", sessionId); + if (sessionId < 0) + { + printf("That certainly isn't a valid ID, they should make a new window.\n"); + createNewWindow = true; + } + else if (sessionId == 0) + { + printf("Session 0 is actually #%llu\n", _mostRecentPeasant); + if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) + { + mruPeasant.ExecuteCommandline(args, cwd); + createNewWindow = false; + } + } + else + { + if (auto otherPeasant = _getPeasant(sessionId)) + { + otherPeasant.ExecuteCommandline(args, cwd); + createNewWindow = false; + } + else + { + printf("I couldn't find a peasant for that ID, they should make a new window.\n"); + } + } + } + } + else if (_windowingBehavior == WindowingBehavior::UseExisting) + { + if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) + { + mruPeasant.ExecuteCommandline(args, cwd); + createNewWindow = false; + } + } + else + { + printf("They definitely weren't an existing process. They should make a new window.\n"); + } + + return createNewWindow; + } + void Monarch::ToggleWindowingBehavior() + { + switch (_windowingBehavior) + { + case WindowingBehavior::UseNew: + _windowingBehavior = WindowingBehavior::UseExisting; + break; + case WindowingBehavior::UseExisting: + _windowingBehavior = WindowingBehavior::UseNew; + break; + } + + printf("windowingBehavior: "); + switch (_windowingBehavior) + { + case WindowingBehavior::UseNew: + printf("useNew"); + break; + case WindowingBehavior::UseExisting: + printf("useExisting"); + break; + } + printf("\n"); + } + +} diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h new file mode 100644 index 00000000000..653ae51976f --- /dev/null +++ b/src/cascadia/Remoting/Monarch.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Monarch.g.h" +#include "Peasant.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +// {06171993-7eb1-4f3e-85f5-8bdd7386cce3} +constexpr GUID Monarch_clsid{ + 0x06171993, + 0x7eb1, + 0x4f3e, + { 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 } +}; + +enum class WindowingBehavior : uint64_t +{ + UseNew = 0, + UseExisting = 1, +}; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct Monarch : public MonarchT + { + Monarch(); + ~Monarch(); + + uint64_t GetPID(); + + uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); + + void SetSelfID(const uint64_t selfID); + + bool ProposeCommandline(array_view args, winrt::hstring cwd); + void ToggleWindowingBehavior(); + + private: + uint64_t _nextPeasantID{ 1 }; + uint64_t _thisPeasantID{ 0 }; + uint64_t _mostRecentPeasant{ 0 }; + WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew }; + std::unordered_map _peasants; + + winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); + void _setMostRecentPeasant(const uint64_t peasantID); + + void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(Monarch); +} diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl new file mode 100644 index 00000000000..8b228480caa --- /dev/null +++ b/src/cascadia/Remoting/Monarch.idl @@ -0,0 +1,12 @@ +import "Peasant.idl"; + +namespace Microsoft.Terminal.Remoting +{ + [default_interface] runtimeclass Monarch { + Monarch(); + + UInt64 GetPID(); + UInt64 AddPeasant(IPeasant peasant); + Boolean ProposeCommandline(String[] args, String cwd); + }; +} diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h new file mode 100644 index 00000000000..9b912995681 --- /dev/null +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -0,0 +1,49 @@ +#include "pch.h" + +#include "Monarch.h" + +// This seems like a hack, but it works. +// +// This class factory works so that there's only ever one instance of a Monarch +// per-process. Once the first monarch is created, we'll stash it in g_weak. +// Future callers who try to instantiate a Monarch will get the one that's +// already been made. +// +// I'm sure there's a better way to do this with WRL, but I'm not familiar +// enough with WRL to know for sure. + +winrt::weak_ref g_weak{ nullptr }; + +struct MonarchFactory : winrt::implements +{ + MonarchFactory() = default; + + HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept + { + *result = nullptr; + if (outer) + { + return CLASS_E_NOAGGREGATION; + } + + if (!g_weak) + { + // Create a new Monarch instance + auto strong = winrt::make_self(); + + g_weak = (*strong).get_weak(); + return strong.as(iid, result); + } + else + { + // We already instantiated one Monarch, let's just return that one! + auto strong = g_weak.get(); + return strong.as(iid, result); + } + } + + HRESULT __stdcall LockServer(BOOL) noexcept + { + return S_OK; + } +}; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp new file mode 100644 index 00000000000..30b10b5f8f9 --- /dev/null +++ b/src/cascadia/Remoting/Peasant.cpp @@ -0,0 +1,51 @@ +#include "pch.h" +#include "Peasant.h" + +#include "Peasant.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + Peasant::Peasant() + { + } + + void Peasant::AssignID(uint64_t id) + { + _id = id; + } + uint64_t Peasant::GetID() + { + return _id; + } + + uint64_t Peasant::GetPID() + { + return GetCurrentProcessId(); + } + + bool Peasant::ExecuteCommandline(winrt::array_view args, winrt::hstring currentDirectory) + { + auto argsProcessed = 0; + std::wstring fullCmdline; + for (const auto& arg : args) + { + fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + fullCmdline += L" "; + } + wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); + wprintf(fullCmdline.c_str()); + wprintf(L"\"\n"); + return true; + } + + void Peasant::raiseActivatedEvent() + { + _WindowActivatedHandlers(*this, nullptr); + } + +} diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h new file mode 100644 index 00000000000..31da53ff5c5 --- /dev/null +++ b/src/cascadia/Remoting/Peasant.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Peasant.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct Peasant : public PeasantT + { + Peasant(); + + void AssignID(uint64_t id); + uint64_t GetID(); + uint64_t GetPID(); + + bool ExecuteCommandline(winrt::array_view args, + winrt::hstring currentDirectory); + + void raiseActivatedEvent(); + + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + + private: + uint64_t _id{ 0 }; + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(Peasant); +} diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl new file mode 100644 index 00000000000..b351448183a --- /dev/null +++ b/src/cascadia/Remoting/Peasant.idl @@ -0,0 +1,18 @@ + + +namespace Microsoft.Terminal.Remoting +{ + interface IPeasant + { + void AssignID(UInt64 id); + UInt64 GetID(); + UInt64 GetPID(); + Boolean ExecuteCommandline(String[] args, String currentDirectory); + event Windows.Foundation.TypedEventHandler WindowActivated; + }; + + [default_interface] runtimeclass Peasant : IPeasant + { + Peasant(); + }; +} diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj index e5077043835..51c6f62ad18 100644 --- a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -48,7 +48,7 @@ - + true true From 5a9cdc8b0bfda790a70368d8381559628eba554f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 09:51:24 -0600 Subject: [PATCH 03/76] Shockingly, this works, it works elevated, and it works unpackaged --- OpenConsole.sln | 192 +++++++++--------- .../Microsoft.Terminal.RemotingLib.vcxproj | 7 + src/cascadia/Remoting/WindowManager.cpp | 67 ++++++ src/cascadia/Remoting/WindowManager.h | 31 +++ src/cascadia/Remoting/WindowManager.idl | 12 ++ src/cascadia/WindowsTerminal/AppHost.cpp | 41 +++- src/cascadia/WindowsTerminal/AppHost.h | 12 +- src/cascadia/WindowsTerminal/main.cpp | 4 + src/cascadia/WindowsTerminal/pch.h | 5 + 9 files changed, 263 insertions(+), 108 deletions(-) create mode 100644 src/cascadia/Remoting/WindowManager.cpp create mode 100644 src/cascadia/Remoting/WindowManager.h create mode 100644 src/cascadia/Remoting/WindowManager.idl diff --git a/OpenConsole.sln b/OpenConsole.sln index 2d5c1deb4f5..e8a479e2876 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -171,18 +171,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalControl", "src\casc EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowsTerminal", "src\cascadia\WindowsTerminal\WindowsTerminal.vcxproj", "{CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}" ProjectSection(ProjectDependencies) = postProject - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} + {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} = {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12} {CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} = {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalApp", "src\cascadia\TerminalApp\dll\TerminalApp.vcxproj", "{CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} - {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} + {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} EndProjectSection @@ -234,8 +235,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\cascadia\TerminalApp\TerminalAppLib.vcxproj", "{CA5CAD1A-9A12-429C-B551-8562EC954746}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} + {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}" @@ -323,8 +324,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} EndProjectSection EndProject - - Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}" ProjectSection(ProjectDependencies) = postProject {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} @@ -343,25 +342,19 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_SettingsModel", {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} EndProjectSection EndProject - - -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting.Lib", "src\cascadia\Remoting\Microsoft.Terminal.RemotingLib.vcxproj", "{43ce4ce5-0010-4b99-9569-672670d26e26}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting.Lib", "src\cascadia\Remoting\Microsoft.Terminal.RemotingLib.vcxproj", "{43CE4CE5-0010-4B99-9569-672670D26E26}" EndProject - -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting", "src\cascadia\Remoting\dll\Microsoft.Terminal.Remoting.vcxproj", "{27b5aaeb-a548-44cf-9777-f8baa32af7ae}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting", "src\cascadia\Remoting\dll\Microsoft.Terminal.Remoting.vcxproj", "{27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}" ProjectSection(ProjectDependencies) = postProject - {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} EndProjectSection EndProject - -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68a10cd3-aa64-465b-af5f-ed4e9700543c}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68A10CD3-AA64-465B-AF5F-ED4E9700543C}" ProjectSection(ProjectDependencies) = postProject - {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {27b5aaeb-a548-44cf-9777-f8baa32af7ae} - {43ce4ce5-0010-4b99-9569-672670d26e26} = {43ce4ce5-0010-4b99-9569-672670d26e26} + {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} EndProjectSection EndProject - - Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2158,87 +2151,84 @@ Global {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x64.Build.0 = Release|x64 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9B68-456A-B13E-C8218070DC42}.Release|x86.Build.0 = Release|Win32 - - - - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x64.ActiveCfg = Release|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.AuditMode|x86.Build.0 = AuditMode|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|ARM64.Build.0 = Debug|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.ActiveCfg = Debug|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x64.Build.0 = Debug|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.ActiveCfg = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Debug|x86.Build.0 = Debug|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|Any CPU.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.ActiveCfg = Release|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|ARM64.Build.0 = Release|ARM64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.ActiveCfg = Release|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x64.Build.0 = Release|x64 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.ActiveCfg = Release|Win32 - {43ce4ce5-0010-4b99-9569-672670d26e26}.Release|x86.Build.0 = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x64.ActiveCfg = Release|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.AuditMode|x86.Build.0 = AuditMode|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|ARM64.Build.0 = Debug|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.ActiveCfg = Debug|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x64.Build.0 = Debug|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.ActiveCfg = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Debug|x86.Build.0 = Debug|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|Any CPU.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.ActiveCfg = Release|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|ARM64.Build.0 = Release|ARM64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.ActiveCfg = Release|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x64.Build.0 = Release|x64 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.ActiveCfg = Release|Win32 - {27b5aaeb-a548-44cf-9777-f8baa32af7ae}.Release|x86.Build.0 = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.AuditMode|x86.Build.0 = AuditMode|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|ARM64.Build.0 = Debug|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.ActiveCfg = Debug|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x64.Build.0 = Debug|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.ActiveCfg = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Debug|x86.Build.0 = Debug|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|Any CPU.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.ActiveCfg = Release|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|ARM64.Build.0 = Release|ARM64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.ActiveCfg = Release|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x64.Build.0 = Release|x64 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.ActiveCfg = Release|Win32 - {68a10cd3-aa64-465b-af5f-ed4e9700543c}.Release|x86.Build.0 = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.Build.0 = AuditMode|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|ARM64.Build.0 = Debug|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x64.ActiveCfg = Debug|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x64.Build.0 = Debug|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x86.ActiveCfg = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|x86.Build.0 = Debug|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|Any CPU.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|ARM64.ActiveCfg = Release|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|ARM64.Build.0 = Release|ARM64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x64.Build.0 = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x86.ActiveCfg = Release|Win32 + {43CE4CE5-0010-4B99-9569-672670D26E26}.Release|x86.Build.0 = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x64.ActiveCfg = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.AuditMode|x86.Build.0 = AuditMode|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|ARM64.Build.0 = Debug|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x64.ActiveCfg = Debug|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x64.Build.0 = Debug|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x86.ActiveCfg = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Debug|x86.Build.0 = Debug|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|Any CPU.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|ARM64.ActiveCfg = Release|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|ARM64.Build.0 = Release|ARM64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x64.ActiveCfg = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x64.Build.0 = Release|x64 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x86.ActiveCfg = Release|Win32 + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE}.Release|x86.Build.0 = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.AuditMode|x86.Build.0 = AuditMode|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|ARM64.Build.0 = Debug|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x64.ActiveCfg = Debug|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x64.Build.0 = Debug|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x86.ActiveCfg = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Debug|x86.Build.0 = Debug|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|Any CPU.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|ARM64.ActiveCfg = Release|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|ARM64.Build.0 = Release|ARM64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|DotNet_x64Test.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x64.ActiveCfg = Release|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x64.Build.0 = Release|x64 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.ActiveCfg = Release|Win32 + {68A10CD3-AA64-465B-AF5F-ED4E9700543C}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2323,9 +2313,9 @@ Global {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} - {43ce4ce5-0010-4b99-9569-672670d26e26} = {59840756-302F-44DF-AA47-441A9D673202} - {27b5aaeb-a548-44cf-9777-f8baa32af7ae} = {59840756-302F-44DF-AA47-441A9D673202} - {68a10cd3-aa64-465b-af5f-ed4e9700543c} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {43CE4CE5-0010-4B99-9569-672670D26E26} = {59840756-302F-44DF-AA47-441A9D673202} + {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {59840756-302F-44DF-AA47-441A9D673202} + {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index bc034434b95..ad736bd6d32 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -24,6 +24,9 @@ Peasant.idl + + WindowManager .idl + @@ -36,12 +39,16 @@ Peasant.idl + + WindowManager.idl + + diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp new file mode 100644 index 00000000000..87e2b482b3e --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -0,0 +1,67 @@ +#include "pch.h" +#include "WindowManager.h" +#include "MonarchFactory.h" + +#include "WindowManager.g.cpp" +#include "../../types/inc/utils.hpp" + +using namespace winrt; +using namespace winrt::Windows::Foundation; +using namespace ::Microsoft::Console; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + WindowManager::WindowManager() + { + _RegisterAsMonarch(); + _CreateMonarch(); + } + WindowManager::~WindowManager() + { + // IMPORTANT! Tear down the registration as soon as we exit. If we're not a + // real peasant window (the monarch passed our commandline to someone else), + // then the monarch dies, we don't want our registration becoming the active + // monarch! + CoRevokeClassObject(_registrationHostClass); + _registrationHostClass = 0; + } + + void WindowManager::ProposeCommandline() + { + _shouldCreateWindow = true; + } + + bool WindowManager::ShouldCreateWindow() + { + return _shouldCreateWindow; + } + + void WindowManager::_RegisterAsMonarch() + { + winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, + winrt::make<::MonarchFactory>().get(), + CLSCTX_LOCAL_SERVER, + REGCLS_MULTIPLEUSE, + &_registrationHostClass)); + } + + void WindowManager::_CreateMonarch() + { + // Heads up! This only works because we're using + // "metadata-based-marshalling" for our WinRT types. THat means the OS is + // using the .winmd file we generate to figure out the proxy/stub + // definitions for our types automatically. This only works in the following + // cases: + // + // * If we're running unpackaged: the .winmd but be a sibling of the .exe + // * If we're running packaged: the .winmd must be in the package root + _monarch = create_instance(Monarch_clsid, + CLSCTX_LOCAL_SERVER); + } + + // bool AppHost::_ProposeCommandlineToMonarch() + // { + // // returns true if we should create a new window + // return true; + // } +} diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h new file mode 100644 index 00000000000..14e4fd33069 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.h @@ -0,0 +1,31 @@ +#pragma once + +#include "WindowManager.g.h" +#include "Peasant.h" +#include "Monarch.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct WindowManager : public WindowManagerT + { + WindowManager(); + ~WindowManager(); + + void ProposeCommandline(); + bool ShouldCreateWindow(); + + private: + bool _shouldCreateWindow{ false }; + DWORD _registrationHostClass{ 0 }; + winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; + + void _RegisterAsMonarch(); + void _CreateMonarch(); + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(WindowManager); +} diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl new file mode 100644 index 00000000000..033602e0e37 --- /dev/null +++ b/src/cascadia/Remoting/WindowManager.idl @@ -0,0 +1,12 @@ + + +namespace Microsoft.Terminal.Remoting +{ + + [default_interface] runtimeclass WindowManager + { + WindowManager(); + void ProposeCommandline(); + Boolean ShouldCreateWindow { get; }; + }; +} diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index a97ba3f46be..40a3784793f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -3,13 +3,12 @@ #include "pch.h" #include "AppHost.h" +// #include "MonarchFactory.h" #include "../types/inc/Viewport.hpp" #include "../types/inc/utils.hpp" #include "../types/inc/User32Utils.hpp" #include "resource.h" -#include - using namespace winrt::Windows::UI; using namespace winrt::Windows::UI::Composition; using namespace winrt::Windows::UI::Xaml; @@ -25,17 +24,26 @@ static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; AppHost::AppHost() noexcept : _app{}, + _windowManager{}, _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 - _useNonClientArea = _logic.GetShowTabsInTitlebar(); + _windowManager.ProposeCommandline(); + // _RegisterAsMonarch(); + // _CreateMonarch(); + _shouldCreateWindow = _windowManager.ShouldCreateWindow(); + if (!_shouldCreateWindow) + { + return; + } // If there were commandline args to our process, try and process them here. // Do this before AppLogic::Create, otherwise this will have no effect - _HandleCommandlineArgs(); + _HandleCommandlineArgs(); // TODO:MG <-- This probably needs to move into _ProposeCommandlineToMonarch + _useNonClientArea = _logic.GetShowTabsInTitlebar(); if (_useNonClientArea) { _window = std::make_unique(_logic.GetRequestedTheme()); @@ -65,6 +73,7 @@ AppHost::AppHost() noexcept : AppHost::~AppHost() { // destruction order is important for proper teardown here + _window = nullptr; _app.Close(); _app = nullptr; @@ -462,3 +471,27 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta) } } } + +bool AppHost::HasWindow() +{ + return _shouldCreateWindow; +} + +// void AppHost::_RegisterAsMonarch() +// { +// winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, +// winrt::make<::MonarchFactory>().get(), +// CLSCTX_LOCAL_SERVER, +// REGCLS_MULTIPLEUSE, +// &_registrationHostClass)); +// } + +// void AppHost::_CreateMonarch() +// { +// } + +// bool AppHost::_ProposeCommandlineToMonarch() +// { +// // returns true if we should create a new window +// return true; +// } diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 25de688131b..e83601be6c5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -3,9 +3,6 @@ #include "pch.h" -#include -#include - #include "NonClientIslandWindow.h" class AppHost @@ -20,12 +17,17 @@ class AppHost bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down); void SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + bool HasWindow(); + private: bool _useNonClientArea; std::unique_ptr _window; winrt::TerminalApp::App _app; winrt::TerminalApp::AppLogic _logic; + bool _shouldCreateWindow{ false }; + // DWORD _registrationHostClass{ 0 }; + winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; void _HandleCommandlineArgs(); @@ -43,4 +45,8 @@ class AppHost void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const til::point coord, const int32_t delta); + + // void _RegisterAsMonarch(); + // void _CreateMonarch(); + // bool _ProposeCommandlineToMonarch(); }; diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index 726ded30444..91188aaca5d 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -124,6 +124,10 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) // Terminal App. This MUST BE constructed before the Xaml manager as TermApp // provides an implementation of Windows.UI.Xaml.Application. AppHost host; + if (!host.HasWindow()) + { + return 0; + } // Initialize the xaml content. This must be called AFTER the // WindowsXamlManager is initialized. diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index e91aeada6e6..b416ccccc4f 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -61,6 +61,11 @@ Module Name: #include #include +#include +#include +#include +#include + #include #include From 36539cfa47d1b5f3a3b0bf44f7bdcb057aa08e95 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 16:03:23 -0600 Subject: [PATCH 04/76] This won't work, but I'm committing this becaus I finally got it to compile a String[] --- src/cascadia/Remoting/CommandlineArgs.cpp | 20 ++++++ src/cascadia/Remoting/CommandlineArgs.h | 39 +++++++++++ .../Microsoft.Terminal.RemotingLib.vcxproj | 8 ++- src/cascadia/Remoting/Monarch.cpp | 13 ++-- src/cascadia/Remoting/Peasant.cpp | 33 ++++++--- src/cascadia/Remoting/Peasant.h | 11 ++- src/cascadia/Remoting/Peasant.idl | 15 +++- src/cascadia/Remoting/WindowManager.cpp | 70 +++++++++++++++---- src/cascadia/Remoting/WindowManager.h | 11 ++- src/cascadia/Remoting/WindowManager.idl | 4 +- src/cascadia/WindowsTerminal/AppHost.cpp | 34 +++++++-- 11 files changed, 217 insertions(+), 41 deletions(-) create mode 100644 src/cascadia/Remoting/CommandlineArgs.cpp create mode 100644 src/cascadia/Remoting/CommandlineArgs.h diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp new file mode 100644 index 00000000000..e3c31cd2a31 --- /dev/null +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -0,0 +1,20 @@ +#include "pch.h" + +#include "CommandlineArgs.h" +#include "CommandlineArgs.g.cpp" +using namespace winrt; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Windows::Foundation; + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + void CommandlineArgs::Args(winrt::array_view const& value) + { + _args = { value.begin(), value.end() }; + } + + winrt::com_array CommandlineArgs::Args() + { + return winrt::com_array{ _args.begin(), _args.end() }; + } +} diff --git a/src/cascadia/Remoting/CommandlineArgs.h b/src/cascadia/Remoting/CommandlineArgs.h new file mode 100644 index 00000000000..159071854f2 --- /dev/null +++ b/src/cascadia/Remoting/CommandlineArgs.h @@ -0,0 +1,39 @@ +#pragma once + +#include "CommandlineArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct CommandlineArgs : public CommandlineArgsT + { + public: + CommandlineArgs() : + _args{}, + _cwd{ L"" } + { + } + + CommandlineArgs(const winrt::array_view& args, + winrt::hstring currentDirectory) : + _args{ args.begin(), args.end() }, + _cwd{ currentDirectory } + { + } + + winrt::hstring CurrentDirectory() { return _cwd; }; + + void Args(winrt::array_view const& value); + winrt::com_array Args(); + + private: + winrt::com_array _args; + winrt::hstring _cwd; + }; + +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(CommandlineArgs); +} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index ad736bd6d32..b5d5355e414 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -25,7 +25,10 @@ Peasant.idl - WindowManager .idl + WindowManager.idl + + + Peasant.idl @@ -42,6 +45,9 @@ WindowManager.idl + + Peasant.idl + diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 6a74171004f..888dbade666 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "Monarch.h" +#include "CommandlineArgs.h" #include "Monarch.g.cpp" #include "../../types/inc/utils.hpp" @@ -91,7 +92,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _setMostRecentPeasant(_thisPeasantID); } - bool Monarch::ProposeCommandline(array_view args, winrt::hstring cwd) + bool Monarch::ProposeCommandline(array_view args, + winrt::hstring cwd) { auto argsProcessed = 0; std::wstring fullCmdline; @@ -125,7 +127,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation printf("Session 0 is actually #%llu\n", _mostRecentPeasant); if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) { - mruPeasant.ExecuteCommandline(args, cwd); + auto eventArgs = winrt::make_self(args, cwd); + mruPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } } @@ -133,7 +136,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { if (auto otherPeasant = _getPeasant(sessionId)) { - otherPeasant.ExecuteCommandline(args, cwd); + auto eventArgs = winrt::make_self(args, cwd); + otherPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } else @@ -147,7 +151,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) { - mruPeasant.ExecuteCommandline(args, cwd); + auto eventArgs = winrt::make_self(args, cwd); + mruPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 30b10b5f8f9..cd8b98fe55a 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -1,10 +1,11 @@ #include "pch.h" #include "Peasant.h" - +#include "CommandlineArgs.h" #include "Peasant.g.cpp" #include "../../types/inc/utils.hpp" using namespace winrt; +using namespace winrt::Microsoft::Terminal; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; @@ -28,18 +29,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return GetCurrentProcessId(); } - bool Peasant::ExecuteCommandline(winrt::array_view args, winrt::hstring currentDirectory) + bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args) { - auto argsProcessed = 0; - std::wstring fullCmdline; - for (const auto& arg : args) + if (_initialArgs == nullptr) { - fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - fullCmdline += L" "; + _initialArgs = args; } - wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); - wprintf(fullCmdline.c_str()); - wprintf(L"\"\n"); + + _ExecuteCommandlineRequestedHandlers(*this, args); + + // auto argsProcessed = 0; + // std::wstring fullCmdline; + // for (const auto& arg : args) + // { + // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + // fullCmdline += L" "; + // } + // wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); + // wprintf(fullCmdline.c_str()); + // wprintf(L"\"\n"); return true; } @@ -48,4 +56,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _WindowActivatedHandlers(*this, nullptr); } + Remoting::CommandlineArgs Peasant::InitialArgs() + { + return _initialArgs; + } + } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 31da53ff5c5..df4db13b529 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -13,15 +13,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t GetID(); uint64_t GetPID(); - bool ExecuteCommandline(winrt::array_view args, - winrt::hstring currentDirectory); + bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void raiseActivatedEvent(); - + winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); private: uint64_t _id{ 0 }; + + winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs; //{ nullptr }; + + // array_view _args; + // winrt::hstring _cwd; }; } diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index b351448183a..7ffb9512170 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -2,13 +2,26 @@ namespace Microsoft.Terminal.Remoting { + + runtimeclass CommandlineArgs + { + CommandlineArgs(); + CommandlineArgs(String[] args, String cwd); + + String[] Args { get; set; }; + String CurrentDirectory(); + }; + interface IPeasant { + CommandlineArgs InitialArgs { get; }; + void AssignID(UInt64 id); UInt64 GetID(); UInt64 GetPID(); - Boolean ExecuteCommandline(String[] args, String currentDirectory); + Boolean ExecuteCommandline(CommandlineArgs args); event Windows.Foundation.TypedEventHandler WindowActivated; + event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; }; [default_interface] runtimeclass Peasant : IPeasant diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 87e2b482b3e..ddc0d85989a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -1,11 +1,13 @@ #include "pch.h" #include "WindowManager.h" #include "MonarchFactory.h" +#include "CommandlineArgs.h" #include "WindowManager.g.cpp" #include "../../types/inc/utils.hpp" using namespace winrt; +using namespace winrt::Microsoft::Terminal; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; @@ -13,8 +15,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowManager::WindowManager() { - _RegisterAsMonarch(); - _CreateMonarch(); + _registerAsMonarch(); + _createMonarch(); } WindowManager::~WindowManager() { @@ -26,8 +28,29 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; } - void WindowManager::ProposeCommandline() + void WindowManager::ProposeCommandline(array_view args, const winrt::hstring cwd) { + const bool isKing = _areWeTheKing(); + // If we're the king, we _definitely_ want to process the arguments, we were + // launched with them! + // + // Otherwise, the King will tell us if we should make a new window + const bool createNewWindow = isKing || + _monarch.ProposeCommandline(args, cwd); + + if (createNewWindow) + { + _createOurPeasant(); + + auto eventArgs = winrt::make_self(args, cwd); + _peasant.ExecuteCommandline(*eventArgs); + _shouldCreateWindow = false; + } + else + { + // printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n"); + } + _shouldCreateWindow = true; } @@ -36,7 +59,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _shouldCreateWindow; } - void WindowManager::_RegisterAsMonarch() + void WindowManager::_registerAsMonarch() { winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, winrt::make<::MonarchFactory>().get(), @@ -45,7 +68,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation &_registrationHostClass)); } - void WindowManager::_CreateMonarch() + void WindowManager::_createMonarch() { // Heads up! This only works because we're using // "metadata-based-marshalling" for our WinRT types. THat means the OS is @@ -55,13 +78,36 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // * If we're running unpackaged: the .winmd but be a sibling of the .exe // * If we're running packaged: the .winmd must be in the package root - _monarch = create_instance(Monarch_clsid, - CLSCTX_LOCAL_SERVER); + _monarch = create_instance(Monarch_clsid, + CLSCTX_LOCAL_SERVER); + } + + bool WindowManager::_areWeTheKing() + { + auto kingPID = _monarch.GetPID(); + auto ourPID = GetCurrentProcessId(); + return (ourPID == kingPID); + } + + Remoting::IPeasant WindowManager::_createOurPeasant() + { + auto p = winrt::make_self(); + _peasant = *p; + auto ourID = _monarch.AddPeasant(_peasant); + ourID; + // printf("The monarch assigned us the ID %llu\n", ourID); + + // if (areWeTheKing()) + // { + // remindKingWhoTheyAre(*peasant); + // } + + return _peasant; + } + + Remoting::Peasant WindowManager::CurrentWindow() + { + return _peasant; } - // bool AppHost::_ProposeCommandlineToMonarch() - // { - // // returns true if we should create a new window - // return true; - // } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 14e4fd33069..1d36ec15bf5 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -12,16 +12,21 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager(); ~WindowManager(); - void ProposeCommandline(); + void ProposeCommandline(array_view args, const winrt::hstring cwd); bool ShouldCreateWindow(); + winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); + private: bool _shouldCreateWindow{ false }; DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; + winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; - void _RegisterAsMonarch(); - void _CreateMonarch(); + void _registerAsMonarch(); + void _createMonarch(); + bool _areWeTheKing(); + winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); }; } diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 033602e0e37..55785aac718 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -1,3 +1,4 @@ +import "Peasant.idl"; namespace Microsoft.Terminal.Remoting @@ -6,7 +7,8 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass WindowManager { WindowManager(); - void ProposeCommandline(); + void ProposeCommandline(String[] commands, String cwd); Boolean ShouldCreateWindow { get; }; + Peasant CurrentWindow(); }; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 40a3784793f..928931ada43 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -30,13 +30,35 @@ AppHost::AppHost() noexcept : { _logic = _app.Logic(); // get a ref to app's logic - _windowManager.ProposeCommandline(); - // _RegisterAsMonarch(); - // _CreateMonarch(); - _shouldCreateWindow = _windowManager.ShouldCreateWindow(); - if (!_shouldCreateWindow) { - return; + std::vector args; + if (auto commandline{ GetCommandLineW() }) + { + int argc = 0; + + // Get the argv, and turn them into a hstring array to pass to the app. + wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + if (argv) + { + for (auto& elem : wil::make_range(argv.get(), argc)) + { + args.emplace_back(elem); + } + } + } + if (args.empty()) + { + args.emplace_back(L"wt.exe"); + } + + _windowManager.ProposeCommandline({ args }, L"placeholder/cwd"); + // _RegisterAsMonarch(); + // _CreateMonarch(); + _shouldCreateWindow = _windowManager.ShouldCreateWindow(); + if (!_shouldCreateWindow) + { + return; + } } // If there were commandline args to our process, try and process them here. From 27ace166529367171fc81cef12aa4812e6ff8e13 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 16 Dec 2020 16:31:51 -0600 Subject: [PATCH 05/76] whoop, we pass the commandline from the peasant, to the monarch, and then back! --- src/cascadia/Remoting/CommandlineArgs.cpp | 4 ++ src/cascadia/Remoting/Monarch.cpp | 22 +++++----- src/cascadia/Remoting/Peasant.h | 2 +- src/cascadia/Remoting/WindowManager.cpp | 3 +- src/cascadia/WindowsTerminal/AppHost.cpp | 51 +++++++++++++++++------ 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index e3c31cd2a31..bbe31961c6e 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -8,6 +8,10 @@ using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Terminal::Remoting::implementation { + // If you try to move this into the header, you will experience P A I N + // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler + // will give you just the most impossible tmplate errors to try and + // decipher. void CommandlineArgs::Args(winrt::array_view const& value) { _args = { value.begin(), value.end() }; diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 888dbade666..9c304b951b7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -95,16 +95,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool Monarch::ProposeCommandline(array_view args, winrt::hstring cwd) { - auto argsProcessed = 0; - std::wstring fullCmdline; - for (const auto& arg : args) - { - fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - fullCmdline += L" "; - } - wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); - wprintf(fullCmdline.c_str()); - wprintf(L"\"\n"); + // auto argsProcessed = 0; + // std::wstring fullCmdline; + // for (const auto& arg : args) + // { + // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; + // fullCmdline += L" "; + // } + // wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); + // wprintf(fullCmdline.c_str()); + // wprintf(L"\"\n"); bool createNewWindow = true; @@ -113,7 +113,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // We'll need three args at least - [WindowsTerminal.exe, -s, // id] to be able to have a session ID passed on the commandline. - if (args[1] == L"-s" || args[1] == L"--session") + if (args[1] == L"-w" || args[1] == L"--window") { auto sessionId = std::stoi({ args[2].data(), args[2].size() }); printf("Found a commandline intended for session %d\n", sessionId); diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index df4db13b529..e2181690e2b 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -23,7 +23,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation private: uint64_t _id{ 0 }; - winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs; //{ nullptr }; + winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; // array_view _args; // winrt::hstring _cwd; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index ddc0d85989a..b5e60723bd2 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -28,7 +28,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; } - void WindowManager::ProposeCommandline(array_view args, const winrt::hstring cwd) + void WindowManager::ProposeCommandline(array_view args, + const winrt::hstring cwd) { const bool isKing = _areWeTheKing(); // If we're the king, we _definitely_ want to process the arguments, we were diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 928931ada43..c37a74e8149 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -142,21 +142,11 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& // - void AppHost::_HandleCommandlineArgs() { - if (auto commandline{ GetCommandLineW() }) + if (auto peasant{ _windowManager.CurrentWindow() }) { - int argc = 0; - - // Get the argv, and turn them into a hstring array to pass to the app. - wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - if (argv) + if (auto args{ peasant.InitialArgs() }) { - std::vector args; - for (auto& elem : wil::make_range(argv.get(), argc)) - { - args.emplace_back(elem); - } - - const auto result = _logic.SetStartupCommandline({ args }); + const auto result = _logic.SetStartupCommandline(args.Args()); const auto message = _logic.ParseCommandlineMessage(); if (!message.empty()) { @@ -177,6 +167,41 @@ void AppHost::_HandleCommandlineArgs() } } } + // if (auto commandline{ GetCommandLineW() }) + // { + // int argc = 0; + + // // Get the argv, and turn them into a hstring array to pass to the app. + // wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + // if (argv) + // { + // std::vector args; + // for (auto& elem : wil::make_range(argv.get(), argc)) + // { + // args.emplace_back(elem); + // } + + // const auto result = _logic.SetStartupCommandline({ args }); + // const auto message = _logic.ParseCommandlineMessage(); + // if (!message.empty()) + // { + // const auto displayHelp = result == 0; + // const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE; + // const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR; + // // TODO:GH#4134: polish this dialog more, to make the text more + // // like msiexec /? + // MessageBoxW(nullptr, + // message.data(), + // GetStringResource(messageTitle).data(), + // MB_OK | messageIcon); + + // if (_logic.ShouldExitEarly()) + // { + // ExitProcess(result); + // } + // } + // } + // } } // Method Description: From 9a41647ffe00d973f11478fb937221239d6ac81a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 07:24:16 -0600 Subject: [PATCH 06/76] HOLY SHIT I GOT THE COMANDLINE TO EXECUTE IN THE CURRENT WINDOW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --- src/cascadia/Remoting/Monarch.cpp | 15 +++++++++++++-- src/cascadia/Remoting/WindowManager.cpp | 5 ++--- src/cascadia/TerminalApp/AppActionHandlers.cpp | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 13 +++++++++++++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 1 + src/cascadia/TerminalApp/TerminalPage.cpp | 6 +++--- src/cascadia/TerminalApp/TerminalPage.h | 3 ++- src/cascadia/WindowsTerminal/AppHost.cpp | 10 ++++++++++ src/cascadia/WindowsTerminal/AppHost.h | 3 +++ 10 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 9c304b951b7..af6e50e53b9 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -117,6 +117,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto sessionId = std::stoi({ args[2].data(), args[2].size() }); printf("Found a commandline intended for session %d\n", sessionId); + + // TODO:MG + // HACK: do an args[2:] to slice off the `-w window` args. + array_view argsNoWindow{ args.begin() + 2, args.end() }; if (sessionId < 0) { printf("That certainly isn't a valid ID, they should make a new window.\n"); @@ -127,7 +131,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation printf("Session 0 is actually #%llu\n", _mostRecentPeasant); if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) { - auto eventArgs = winrt::make_self(args, cwd); + // TODO In the morning: + // Right now, this commandline includes the "-w window" param, and CLI11 is biting it when parsing that. + // Either: + // * hack yank it for the time being (args[2:]) + // * actually have an AppCommandlineArgs do the parsing. + + auto eventArgs = winrt::make_self(argsNoWindow, cwd); mruPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } @@ -136,7 +146,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { if (auto otherPeasant = _getPeasant(sessionId)) { - auto eventArgs = winrt::make_self(args, cwd); + auto eventArgs = winrt::make_self(argsNoWindow, cwd); otherPeasant.ExecuteCommandline(*eventArgs); createNewWindow = false; } @@ -163,6 +173,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return createNewWindow; } + void Monarch::ToggleWindowingBehavior() { switch (_windowingBehavior) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index b5e60723bd2..5ba26eada7a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -45,14 +45,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto eventArgs = winrt::make_self(args, cwd); _peasant.ExecuteCommandline(*eventArgs); - _shouldCreateWindow = false; + _shouldCreateWindow = true; } else { // printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n"); + _shouldCreateWindow = false; } - - _shouldCreateWindow = true; } bool WindowManager::ShouldCreateWindow() diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index f51ea8c2ae2..c7c4d028a42 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -453,7 +453,7 @@ namespace winrt::TerminalApp::implementation if (_startupActions.Size() != 0) { actionArgs.Handled(true); - _ProcessStartupActions(actions, false); + ProcessStartupActions(actions, false); } } } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index f052341b53f..e581d1f987c 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1111,6 +1111,19 @@ namespace winrt::TerminalApp::implementation return result; } + int32_t AppLogic::ExecuteCommandline(array_view args) + { + ::TerminalApp::AppCommandlineArgs appArgs; + auto result = appArgs.ParseArgs(args); + if (result == 0) + { + auto actions = winrt::single_threaded_vector(std::move(appArgs.GetStartupActions())); + _root->ProcessStartupActions(actions, false); + } + + return result; // TODO:MG does a return value make sense + } + // Method Description: // - If there were any errors parsing the commandline that was used to // initialize the terminal, this will return a string containing that diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 3d2051a1200..1a93abcf509 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -29,6 +29,7 @@ namespace winrt::TerminalApp::implementation [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; int32_t SetStartupCommandline(array_view actions); + int32_t ExecuteCommandline(array_view actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index d1bc606d4b8..3abf62eb12a 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -29,6 +29,7 @@ namespace TerminalApp Boolean IsElevated(); Int32 SetStartupCommandline(String[] commands); + Int32 ExecuteCommandline(String[] commands); String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index a21779e263c..adfbb3a46c1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -331,7 +331,7 @@ namespace winrt::TerminalApp::implementation } else { - _ProcessStartupActions(_startupActions, true); + ProcessStartupActions(_startupActions, true); } } } @@ -347,8 +347,8 @@ namespace winrt::TerminalApp::implementation // should fire an Initialized event. // Return Value: // - - winrt::fire_and_forget TerminalPage::_ProcessStartupActions(Windows::Foundation::Collections::IVector actions, - const bool initial) + winrt::fire_and_forget TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector actions, + const bool initial) { // If there are no actions left, do nothing. if (actions.Size() == 0) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 3edc7ac40ed..407dd46958a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -81,6 +81,8 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); + winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); + // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); @@ -137,7 +139,6 @@ namespace winrt::TerminalApp::implementation StartupState _startupState{ StartupState::NotInitialized }; Windows::Foundation::Collections::IVector _startupActions; - winrt::fire_and_forget _ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); void _ShowAboutDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseWarningDialog(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index c37a74e8149..346f5d127ee 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -59,6 +59,10 @@ AppHost::AppHost() noexcept : { return; } + if (auto peasant{ _windowManager.CurrentWindow() }) + { + peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); + } } // If there were commandline args to our process, try and process them here. @@ -542,3 +546,9 @@ bool AppHost::HasWindow() // // returns true if we should create a new window // return true; // } + +void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, + winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) +{ + _logic.ExecuteCommandline(args.Args()); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index e83601be6c5..22dbbc2c113 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -49,4 +49,7 @@ class AppHost // void _RegisterAsMonarch(); // void _CreateMonarch(); // bool _ProposeCommandlineToMonarch(); + + void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, + winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); }; From 5cabcfb45229f686d424c6d5a2fdfca7dd446639 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 07:43:46 -0600 Subject: [PATCH 07/76] add a note to future me --- src/cascadia/WindowsTerminal/AppHost.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 346f5d127ee..0eb9418cd09 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -63,6 +63,12 @@ AppHost::AppHost() noexcept : { peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); } + + // TODO:MG if we end up not creating a new window, we crash. I'm + // thinking this is because the XAML host is not happy about being torn + // down before it has a chance to do really anything. Is there some way + // to get the app logic without instantiating the entire app? or at + // least the parts we'll need for remoting? } // If there were commandline args to our process, try and process them here. From 03bfc6e8a93c901ef00b34310c4e1c313019e720 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 10:50:48 -0600 Subject: [PATCH 08/76] This works as a unittest, but not a local test. That's batty --- OpenConsole.sln | 1 + build/Helix/runtests.cmd | 6 +- .../templates/helix-runtests-job.yml | 42 ++- .../LocalTests_Remoting/RemotingTests.cpp | 289 ++---------------- .../TestHostApp/TestHostApp.vcxproj | 4 + src/cascadia/Remoting/Monarch.cpp | 10 +- src/cascadia/Remoting/Monarch.h | 10 + tools/OpenConsole.psm1 | 2 +- tools/runut.cmd | 4 + tools/tests.xml | 2 + 10 files changed, 90 insertions(+), 280 deletions(-) diff --git a/OpenConsole.sln b/OpenConsole.sln index e8a479e2876..e94b662f4ac 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -262,6 +262,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {CA5CAD1A-9B68-456A-B13E-C8218070DC42} + {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {68A10CD3-AA64-465B-AF5F-ED4E9700543C} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDB237B6-1D1D-400F-84CC-40A58FA59C8E}" diff --git a/build/Helix/runtests.cmd b/build/Helix/runtests.cmd index ddf8c2d3d11..3b155e279a9 100644 --- a/build/Helix/runtests.cmd +++ b/build/Helix/runtests.cmd @@ -18,7 +18,7 @@ FOR %%A IN (TestHostApp.exe,te.exe,te.processhost.exe,conhost.exe,OpenConsole.ex echo %TIME% -:: kill dhandler, which is a tool designed to handle unexpected windows appearing. But since our tests are +:: kill dhandler, which is a tool designed to handle unexpected windows appearing. But since our tests are :: expected to show UI we don't want it running. taskkill -f -im dhandler.exe @@ -28,7 +28,7 @@ echo %TIME% powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1 echo %TIME% -set testBinaryCandidates=TerminalApp.LocalTests.dll Conhost.UIA.Tests.dll +set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Remoting.LocalTests.dll Conhost.UIA.Tests.dll set testBinaries= for %%B in (%testBinaryCandidates%) do ( if exist %%B ( @@ -103,4 +103,4 @@ copy /y *_subresults.json %HELIX_WORKITEM_UPLOAD_ROOT% type testResults.xml -echo %TIME% \ No newline at end of file +echo %TIME% diff --git a/build/pipelines/templates/helix-runtests-job.yml b/build/pipelines/templates/helix-runtests-job.yml index 81125f3d988..736bfc5338e 100644 --- a/build/pipelines/templates/helix-runtests-job.yml +++ b/build/pipelines/templates/helix-runtests-job.yml @@ -5,14 +5,14 @@ parameters: testSuite: '' # If a Pipeline runs this template more than once, this parameter should be unique per build flavor to differentiate the # the different test runs: - helixType: 'test/devtest' + helixType: 'test/devtest' artifactName: 'drop' maxParallel: 4 rerunPassesRequiredToAvoidFailure: 5 taefQuery: '' # if 'useBuildOutputFromBuildId' is set, we will default to using a build from this pipeline: useBuildOutputFromPipeline: $(System.DefinitionId) - matrix: + matrix: # Release_x86: # buildPlatform: 'x86' # buildConfiguration: 'release' @@ -39,13 +39,13 @@ jobs: taefPath: $(Build.SourcesDirectory)\build\Helix\packages\taef.redist.wlk.10.57.200731005-develop\build\Binaries\$(buildPlatform) helixCommonArgs: '/binaryLogger:$(Build.SourcesDirectory)/${{parameters.name}}.$(buildPlatform).$(buildConfiguration).binlog /p:HelixBuild=$(Build.BuildId).$(buildPlatform).$(buildConfiguration) /p:Platform=$(buildPlatform) /p:Configuration=$(buildConfiguration) /p:HelixType=${{parameters.helixType}} /p:TestSuite=${{parameters.testSuite}} /p:ProjFilesPath=$(Build.ArtifactStagingDirectory) /p:rerunPassesRequiredToAvoidFailure=${{parameters.rerunPassesRequiredToAvoidFailure}}' - + steps: - task: CmdLine@1 displayName: 'Display build machine environment variables' inputs: filename: 'set' - + - task: NuGetToolInstaller@0 displayName: 'Use NuGet 5.2.0' inputs: @@ -59,23 +59,23 @@ jobs: nugetConfigPath: nuget.config restoreDirectory: packages - - task: DownloadBuildArtifacts@0 + - task: DownloadBuildArtifacts@0 condition: and(succeeded(),eq(variables['useBuildOutputFromBuildId'],'')) - inputs: - artifactName: ${{ parameters.artifactName }} + inputs: + artifactName: ${{ parameters.artifactName }} downloadPath: '$(artifactsDir)' - - task: DownloadBuildArtifacts@0 + - task: DownloadBuildArtifacts@0 condition: and(succeeded(),ne(variables['useBuildOutputFromBuildId'],'')) - inputs: + inputs: buildType: specific buildVersionToDownload: specific project: $(System.TeamProjectId) pipeline: ${{ parameters.useBuildOutputFromPipeline }} buildId: $(useBuildOutputFromBuildId) - artifactName: ${{ parameters.artifactName }} + artifactName: ${{ parameters.artifactName }} downloadPath: '$(artifactsDir)' - task: CmdLine@1 @@ -90,7 +90,7 @@ jobs: targetType: filePath filePath: build\Helix\PrepareHelixPayload.ps1 arguments: -Platform '$(buildPlatform)' -Configuration '$(buildConfiguration)' -ArtifactName '${{ parameters.artifactName }}' - + - task: CmdLine@1 displayName: 'Display Helix payload contents' inputs: @@ -104,7 +104,23 @@ jobs: outputProjFileName: 'RunTestsInHelix-TerminalAppLocalTests.proj' testSuite: '${{ parameters.testSuite }}' taefQuery: ${{ parameters.taefQuery }} - + + - template: helix-createprojfile-steps.yml + parameters: + condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) + testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\SettingsModel.LocalTests.dll' + outputProjFileName: 'RunTestsInHelix-SettingsModelLocalTests.proj' + testSuite: '${{ parameters.testSuite }}' + taefQuery: ${{ parameters.taefQuery }} + + - template: helix-createprojfile-steps.yml + parameters: + condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) + testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\Remoting.LocalTests.dll' + outputProjFileName: 'RunTestsInHelix-RemotingLocalTests.proj' + testSuite: '${{ parameters.testSuite }}' + taefQuery: ${{ parameters.taefQuery }} + - template: helix-createprojfile-steps.yml parameters: condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) @@ -118,7 +134,7 @@ jobs: inputs: PathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: ${{ parameters.artifactName }} - + - task: DotNetCoreCLI@2 displayName: 'Run tests in Helix (open queues)' env: diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp index 60491de9cbf..008cc62579d 100644 --- a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp @@ -2,294 +2,63 @@ // Licensed under the MIT license. #include "pch.h" - -#include "../TerminalSettingsModel/ColorScheme.h" -#include "../TerminalSettingsModel/CascadiaSettings.h" -#include "JsonTestClass.h" -#include "TestUtils.h" -#include -#include "../ut_app/TestDynamicProfileGenerator.h" +#include "../Remoting/Monarch.h" using namespace Microsoft::Console; using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace WEX::Common; -using namespace winrt::Microsoft::Terminal::Settings::Model; -using namespace winrt::Microsoft::Terminal::TerminalControl; -namespace SettingsModelLocalTests +using namespace winrt; +using namespace winrt::Microsoft::Terminal; + +namespace RemotingLocalTests { // TODO:microsoft/terminal#3838: // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for // an updated TAEF that will let us install framework packages when the test // package is deployed. Until then, these tests won't deploy in CI. - class SerializationTests : public JsonTestClass + class RemotingTests { // Use a custom AppxManifest to ensure that we can activate winrt types // from our test. This property will tell taef to manually use this as // the AppxManifest for this test class. // This does not yet work for anything XAML-y. See TabTests.cpp for more // details on that. - BEGIN_TEST_CLASS(SerializationTests) - TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") + BEGIN_TEST_CLASS(RemotingTests) + // TEST_CLASS_PROPERTY(L"RunAs", L"UAP") + // TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") END_TEST_CLASS() - TEST_METHOD(GlobalSettings); - TEST_METHOD(Profile); - TEST_METHOD(ColorScheme); - TEST_METHOD(CascadiaSettings); + TEST_METHOD(CreateMonarch); TEST_CLASS_SETUP(ClassSetup) { - InitializeJsonReader(); - InitializeJsonWriter(); return true; } - - private: - // Method Description: - // - deserializes and reserializes a json string representing a settings object model of type T - // - verifies that the generated json string matches the provided one - // Template Types: - // - : The type of Settings Model object to generate (must be impl type) - // Arguments: - // - jsonString - JSON string we're performing the test on - // Return Value: - // - the JsonObject representing this instance - template - void RoundtripTest(const std::string& jsonString) - { - const auto json{ VerifyParseSucceeded(jsonString) }; - const auto settings{ T::FromJson(json) }; - const auto result{ settings->ToJson() }; - - // Compare toString(json) instead of jsonString here. - // The toString writes the json out alphabetically. - // This trick allows jsonString to _not_ have to be - // written alphabetically. - VERIFY_ARE_EQUAL(toString(json), toString(result)); - } }; - void SerializationTests::GlobalSettings() - { - const std::string globalsString{ R"( - { - "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", - - "initialRows": 30, - "initialCols": 120, - "initialPosition": ",", - "launchMode": "default", - "alwaysOnTop": false, - - "copyOnSelect": false, - "copyFormatting": "all", - "wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502", - - "alwaysShowTabs": true, - "showTabsInTitlebar": true, - "showTerminalTitleInTitlebar": true, - "tabWidthMode": "equal", - "tabSwitcherMode": "mru", - - "startOnUserLogin": false, - "theme": "system", - "snapToGridOnResize": true, - "disableAnimations": false, - - "confirmCloseAllTabs": true, - "largePasteWarning": true, - "multiLinePasteWarning": true, - - "experimental.input.forceVT": false, - "experimental.rendering.forceFullRepaint": false, - "experimental.rendering.software": false - })" }; - - const std::string smallGlobalsString{ R"( - { - "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}" - })" }; - - RoundtripTest(globalsString); - RoundtripTest(smallGlobalsString); - } - - void SerializationTests::Profile() - { - const std::string profileString{ R"( - { - "name": "Windows PowerShell", - "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", - - "commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", - "startingDirectory": "%USERPROFILE%", - - "icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png", - "hidden": false, - - "tabTitle": "Cool Tab", - "suppressApplicationTitle": false, - - "fontFace": "Cascadia Mono", - "fontSize": 12, - "fontWeight": "normal", - "padding": "8, 8, 8, 8", - "antialiasingMode": "grayscale", - - "cursorShape": "bar", - "cursorColor": "#CCBBAA", - "cursorHeight": 10, - - "altGrAliasing": true, - - "colorScheme": "Campbell", - "tabColor": "#0C0C0C", - "foreground": "#AABBCC", - "background": "#BBCCAA", - "selectionBackground": "#CCAABB", - - "useAcrylic": false, - "acrylicOpacity": 0.5, - - "backgroundImage": "made_you_look.jpeg", - "backgroundImageStretchMode": "uniformToFill", - "backgroundImageAlignment": "center", - "backgroundImageOpacity": 1.0, - - "scrollbarState": "visible", - "snapOnInput": true, - "historySize": 9001, - - "closeOnExit": "graceful", - "experimental.retroTerminalEffect": false - })" }; - - const std::string smallProfileString{ R"( - { - "name": "Custom Profile" - })" }; - - // Setting "tabColor" to null tests two things: - // - null should count as an explicit user-set value, not falling back to the parent's value - // - null should be acceptable even though we're working with colors - const std::string weirdProfileString{ R"( - { - "name": "Weird Profile", - "tabColor": null, - "foreground": null, - "source": "local" - })" }; - - RoundtripTest(profileString); - RoundtripTest(smallProfileString); - RoundtripTest(weirdProfileString); - } - - void SerializationTests::ColorScheme() + void RemotingTests::CreateMonarch() { - const std::string schemeString{ R"({ - "name": "Campbell", - - "cursorColor": "#FFFFFF", - "selectionBackground": "#131313", - - "background": "#0C0C0C", - "foreground": "#F2F2F2", - - "black": "#0C0C0C", - "blue": "#0037DA", - "cyan": "#3A96DD", - "green": "#13A10E", - "purple": "#881798", - "red": "#C50F1F", - "white": "#CCCCCC", - "yellow": "#C19C00", - "brightBlack": "#767676", - "brightBlue": "#3B78FF", - "brightCyan": "#61D6D6", - "brightGreen": "#16C60C", - "brightPurple": "#B4009E", - "brightRed": "#E74856", - "brightWhite": "#F2F2F2", - "brightYellow": "#F9F1A5" - })" }; - - RoundtripTest(schemeString); + auto m1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(m1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + m1->GetPID(), + L"A Monarch without an explicit PID should use the current PID"); + + Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); + + auto expectedFakePID = 1234u; + Remoting::implementation::Monarch foo{ expectedFakePID }; + com_ptr m2; + m2.attach(&foo); + auto cleanup = wil::scope_exit([&]() { m2.detach(); }); + // auto m2 = winrt::make_self(expectedFakePID); + VERIFY_IS_NOT_NULL(m2); + VERIFY_ARE_EQUAL(expectedFakePID, + m2->GetPID(), + L"A Monarch with an explicit PID should use the one we provided"); } - void SerializationTests::CascadiaSettings() - { - const std::string settingsString{ R"({ - "$schema": "https://aka.ms/terminal-profiles-schema", - "defaultProfile": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", - - "profiles": { - "defaults": { - "fontFace": "Zamora Code" - }, - "list": [ - { - "fontFace": "Cascadia Code", - "guid": "{61c54bbd-1111-5271-96e7-009a87ff44bf}", - "name": "HowettShell" - }, - { - "hidden": true, - "name": "BhojwaniShell" - }, - { - "antialiasingMode": "aliased", - "name": "NiksaShell" - } - ] - }, - "schemes": [ - { - "name": "Cinnamon Roll", - - "cursorColor": "#FFFFFD", - "selectionBackground": "#FFFFFF", - - "background": "#3C0315", - "foreground": "#FFFFFD", - - "black": "#282A2E", - "blue": "#0170C5", - "cyan": "#3F8D83", - "green": "#76AB23", - "purple": "#7D498F", - "red": "#BD0940", - "white": "#FFFFFD", - "yellow": "#E0DE48", - "brightBlack": "#676E7A", - "brightBlue": "#5C98C5", - "brightCyan": "#8ABEB7", - "brightGreen": "#B5D680", - "brightPurple": "#AC79BB", - "brightRed": "#BD6D85", - "brightWhite": "#FFFFFD", - "brightYellow": "#FFFD76" - } - ], - "actions": [ - {"command": { "action": "renameTab","input": "Liang Tab" },"keys": "ctrl+t" } - ], - "keybindings": [ - { "command": { "action": "sendInput","input": "VT Griese Mode" },"keys": "ctrl+k" } - ] - })" }; - - auto settings{ winrt::make_self(false) }; - settings->_ParseJsonString(settingsString, false); - settings->_ApplyDefaultsFromUserSettings(); - settings->LayerJson(settings->_userSettings); - settings->_ValidateSettings(); - - const auto result{ settings->ToJson() }; - VERIFY_ARE_EQUAL(toString(settings->_userSettings), toString(result)); - } } diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 384356df6f5..99194c82db8 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -107,6 +107,9 @@ {CA5CAD1A-082C-4476-9F33-94B339494076} + + {27b5aaeb-a548-44cf-9777-f8baa32af7ae} + @@ -132,6 +135,7 @@ + diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index af6e50e53b9..6b124dbd0b9 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -11,9 +11,13 @@ using namespace ::Microsoft::Console; namespace winrt::Microsoft::Terminal::Remoting::implementation { - Monarch::Monarch() + Monarch::Monarch() : + _ourPID{ GetCurrentProcessId() } + { + } + Monarch::Monarch(const uint64_t testPID) : + _ourPID{ testPID } { - printf("Instantiated a Monarch\n"); } Monarch::~Monarch() @@ -23,7 +27,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Monarch::GetPID() { - return GetCurrentProcessId(); + return _ourPID; } uint64_t Monarch::AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant) diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 653ae51976f..1069bb0f3af 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -18,6 +18,11 @@ enum class WindowingBehavior : uint64_t UseExisting = 1, }; +namespace RemotingLocalTests +{ + class RemotingTests; +}; + namespace winrt::Microsoft::Terminal::Remoting::implementation { struct Monarch : public MonarchT @@ -35,6 +40,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void ToggleWindowingBehavior(); private: + Monarch(const uint64_t testPID); + uint64_t _ourPID; + uint64_t _nextPeasantID{ 1 }; uint64_t _thisPeasantID{ 0 }; uint64_t _mostRecentPeasant{ 0 }; @@ -46,6 +54,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + + friend class RemotingLocalTests::RemotingTests; }; } diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index 3f628e4235c..fda169365c4 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -159,7 +159,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'localRemoting')] [string]$Test, [parameter(Mandatory=$false)] diff --git a/tools/runut.cmd b/tools/runut.cmd index 9df937cf1a9..c6cec1fd12b 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -23,6 +23,10 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Types.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\til.unit.tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^ + %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\LocalTests_Remoting\Remoting.LocalTests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ + %_TestHostAppPath%\SettingsModel.LocalTests.dll ^ %* + + rem %_TestHostAppPath%\Remoting.LocalTests.dll ^ diff --git a/tools/tests.xml b/tools/tests.xml index 437145b0517..74beb621f15 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -5,6 +5,8 @@ + + From 590b9ff0c73487720a110213c4b19a2f4a00be8e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 11:01:52 -0600 Subject: [PATCH 09/76] this macro makes me feel dirty --- src/cascadia/LocalTests_Remoting/RemotingTests.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp index 008cc62579d..b8169289950 100644 --- a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/LocalTests_Remoting/RemotingTests.cpp @@ -12,6 +12,12 @@ using namespace WEX::Common; using namespace winrt; using namespace winrt::Microsoft::Terminal; +#define MAKE_MONARCH(name, pid) \ + Remoting::implementation::Monarch _local_##name##{ pid }; \ + com_ptr name; \ + name.attach(&_local_##name##); \ + auto cleanup = wil::scope_exit([&]() { name.detach(); }); + namespace RemotingLocalTests { // TODO:microsoft/terminal#3838: @@ -50,11 +56,8 @@ namespace RemotingLocalTests Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); auto expectedFakePID = 1234u; - Remoting::implementation::Monarch foo{ expectedFakePID }; - com_ptr m2; - m2.attach(&foo); - auto cleanup = wil::scope_exit([&]() { m2.detach(); }); - // auto m2 = winrt::make_self(expectedFakePID); + MAKE_MONARCH(m2, expectedFakePID); + VERIFY_IS_NOT_NULL(m2); VERIFY_ARE_EQUAL(expectedFakePID, m2->GetPID(), From 0579b2417bcdd74814ea05d7eb64723542584764 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 11:10:09 -0600 Subject: [PATCH 10/76] LocalTests_Remoting -> UnitTests_Remoting --- .../TestHostApp/TestHostApp.vcxproj | 4 --- src/cascadia/Remoting/Monarch.cpp | 25 ------------------- src/cascadia/Remoting/Monarch.h | 5 ++-- .../LocalTests_Remoting.def | 0 .../Remoting.LocalTests.vcxproj | 0 .../RemotingTests.cpp | 14 +---------- .../pch.cpp | 0 .../pch.h | 0 tools/runut.cmd | 4 +-- 9 files changed, 4 insertions(+), 48 deletions(-) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/LocalTests_Remoting.def (100%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/Remoting.LocalTests.vcxproj (100%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/RemotingTests.cpp (66%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/pch.cpp (100%) rename src/cascadia/{LocalTests_Remoting => UnitTests_Remoting}/pch.h (100%) diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 99194c82db8..384356df6f5 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -107,9 +107,6 @@ {CA5CAD1A-082C-4476-9F33-94B339494076} - - {27b5aaeb-a548-44cf-9777-f8baa32af7ae} - @@ -135,7 +132,6 @@ - diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 6b124dbd0b9..70b02d60191 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -178,29 +178,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return createNewWindow; } - void Monarch::ToggleWindowingBehavior() - { - switch (_windowingBehavior) - { - case WindowingBehavior::UseNew: - _windowingBehavior = WindowingBehavior::UseExisting; - break; - case WindowingBehavior::UseExisting: - _windowingBehavior = WindowingBehavior::UseNew; - break; - } - - printf("windowingBehavior: "); - switch (_windowingBehavior) - { - case WindowingBehavior::UseNew: - printf("useNew"); - break; - case WindowingBehavior::UseExisting: - printf("useExisting"); - break; - } - printf("\n"); - } - } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 1069bb0f3af..a79cd7754fd 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -18,7 +18,7 @@ enum class WindowingBehavior : uint64_t UseExisting = 1, }; -namespace RemotingLocalTests +namespace RemotingUnitTests { class RemotingTests; }; @@ -37,7 +37,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void SetSelfID(const uint64_t selfID); bool ProposeCommandline(array_view args, winrt::hstring cwd); - void ToggleWindowingBehavior(); private: Monarch(const uint64_t testPID); @@ -55,7 +54,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); - friend class RemotingLocalTests::RemotingTests; + friend class RemotingUnitTests::RemotingTests; }; } diff --git a/src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def b/src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def similarity index 100% rename from src/cascadia/LocalTests_Remoting/LocalTests_Remoting.def rename to src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def diff --git a/src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj b/src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj similarity index 100% rename from src/cascadia/LocalTests_Remoting/Remoting.LocalTests.vcxproj rename to src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj diff --git a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp similarity index 66% rename from src/cascadia/LocalTests_Remoting/RemotingTests.cpp rename to src/cascadia/UnitTests_Remoting/RemotingTests.cpp index b8169289950..ac96debf541 100644 --- a/src/cascadia/LocalTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -18,23 +18,11 @@ using namespace winrt::Microsoft::Terminal; name.attach(&_local_##name##); \ auto cleanup = wil::scope_exit([&]() { name.detach(); }); -namespace RemotingLocalTests +namespace RemotingUnitTests { - // TODO:microsoft/terminal#3838: - // Unfortunately, these tests _WILL NOT_ work in our CI. We're waiting for - // an updated TAEF that will let us install framework packages when the test - // package is deployed. Until then, these tests won't deploy in CI. - class RemotingTests { - // Use a custom AppxManifest to ensure that we can activate winrt types - // from our test. This property will tell taef to manually use this as - // the AppxManifest for this test class. - // This does not yet work for anything XAML-y. See TabTests.cpp for more - // details on that. BEGIN_TEST_CLASS(RemotingTests) - // TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - // TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TestHostAppXManifest.xml") END_TEST_CLASS() TEST_METHOD(CreateMonarch); diff --git a/src/cascadia/LocalTests_Remoting/pch.cpp b/src/cascadia/UnitTests_Remoting/pch.cpp similarity index 100% rename from src/cascadia/LocalTests_Remoting/pch.cpp rename to src/cascadia/UnitTests_Remoting/pch.cpp diff --git a/src/cascadia/LocalTests_Remoting/pch.h b/src/cascadia/UnitTests_Remoting/pch.h similarity index 100% rename from src/cascadia/LocalTests_Remoting/pch.h rename to src/cascadia/UnitTests_Remoting/pch.h diff --git a/tools/runut.cmd b/tools/runut.cmd index c6cec1fd12b..7b7571c678b 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -23,10 +23,8 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Types.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\til.unit.tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^ - %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\LocalTests_Remoting\Remoting.LocalTests.dll ^ + %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_Remoting\Remoting.UnitTests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ %_TestHostAppPath%\SettingsModel.LocalTests.dll ^ %* - - rem %_TestHostAppPath%\Remoting.LocalTests.dll ^ From a3faed6b7d82049b7f43162bfd88bd8534d7de15 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 11:15:57 -0600 Subject: [PATCH 11/76] finish renaming this test --- OpenConsole.sln | 3 +-- build/pipelines/templates/helix-runtests-job.yml | 7 ------- ...oting.LocalTests.vcxproj => Remoting.UnitTests.vcxproj} | 6 +++--- .../{LocalTests_Remoting.def => UnitTests_Remoting.def} | 0 src/cascadia/UnitTests_Remoting/pch.h | 1 - tools/OpenConsole.psm1 | 2 +- tools/tests.xml | 2 +- 7 files changed, 6 insertions(+), 15 deletions(-) rename src/cascadia/UnitTests_Remoting/{Remoting.LocalTests.vcxproj => Remoting.UnitTests.vcxproj} (93%) rename src/cascadia/UnitTests_Remoting/{LocalTests_Remoting.def => UnitTests_Remoting.def} (100%) diff --git a/OpenConsole.sln b/OpenConsole.sln index e94b662f4ac..06bfcb492b7 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -262,7 +262,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestHostApp", "src\cascadia {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} {CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076} {CA5CAD1A-9B68-456A-B13E-C8218070DC42} = {CA5CAD1A-9B68-456A-B13E-C8218070DC42} - {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {68A10CD3-AA64-465B-AF5F-ED4E9700543C} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{BDB237B6-1D1D-400F-84CC-40A58FA59C8E}" @@ -350,7 +349,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Remoting {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_Remoting", "src\cascadia\LocalTests_Remoting\Remoting.LocalTests.vcxproj", "{68A10CD3-AA64-465B-AF5F-ED4E9700543C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Remoting", "src\cascadia\UnitTests_Remoting\Remoting.UnitTests.vcxproj", "{68A10CD3-AA64-465B-AF5F-ED4E9700543C}" ProjectSection(ProjectDependencies) = postProject {43CE4CE5-0010-4B99-9569-672670D26E26} = {43CE4CE5-0010-4B99-9569-672670D26E26} {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} diff --git a/build/pipelines/templates/helix-runtests-job.yml b/build/pipelines/templates/helix-runtests-job.yml index 736bfc5338e..04169e342aa 100644 --- a/build/pipelines/templates/helix-runtests-job.yml +++ b/build/pipelines/templates/helix-runtests-job.yml @@ -113,13 +113,6 @@ jobs: testSuite: '${{ parameters.testSuite }}' taefQuery: ${{ parameters.taefQuery }} - - template: helix-createprojfile-steps.yml - parameters: - condition: and(succeeded(),ne('${{ parameters.testSuite }}','NugetTestSuite')) - testFilePath: '$(artifactsDir)\${{ parameters.artifactName }}\$(buildConfiguration)\$(buildPlatform)\Test\Remoting.LocalTests.dll' - outputProjFileName: 'RunTestsInHelix-RemotingLocalTests.proj' - testSuite: '${{ parameters.testSuite }}' - taefQuery: ${{ parameters.taefQuery }} - template: helix-createprojfile-steps.yml parameters: diff --git a/src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj similarity index 93% rename from src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj rename to src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj index c91a5d003e8..a151a594027 100644 --- a/src/cascadia/UnitTests_Remoting/Remoting.LocalTests.vcxproj +++ b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj @@ -14,9 +14,9 @@ {68a10cd3-aa64-465b-af5f-ed4e9700543c} Win32Proj - RemotingLocalTests - LocalTests_Remoting - Remoting.LocalTests + RemotingUnitTests + UnitTests_Remoting + Remoting.UnitTests DynamicLibrary 10.0.18362.0 10.0.18362.0 diff --git a/src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def b/src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def similarity index 100% rename from src/cascadia/UnitTests_Remoting/LocalTests_Remoting.def rename to src/cascadia/UnitTests_Remoting/UnitTests_Remoting.def diff --git a/src/cascadia/UnitTests_Remoting/pch.h b/src/cascadia/UnitTests_Remoting/pch.h index a88cde20a9c..da79b2d4fd8 100644 --- a/src/cascadia/UnitTests_Remoting/pch.h +++ b/src/cascadia/UnitTests_Remoting/pch.h @@ -22,7 +22,6 @@ Licensed under the MIT license. #include #include -#include #include "consoletaeftemplates.hpp" #include diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index fda169365c4..fd205c2ba51 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -159,7 +159,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'localRemoting')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp', 'localSettingsModel', 'unitRemoting')] [string]$Test, [parameter(Mandatory=$false)] diff --git a/tools/tests.xml b/tools/tests.xml index 74beb621f15..0a296e21f6d 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -6,7 +6,7 @@ - + From 9c6eac4e86e387d19e3d1073d16eedde014eadbb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 12:39:59 -0600 Subject: [PATCH 12/76] Clean up a lot for review --- src/cascadia/Remoting/Monarch.cpp | 121 ++++++-------------- src/cascadia/Remoting/WindowManager.cpp | 29 ++--- src/cascadia/WindowsTerminal/AppHost.cpp | 136 ++++++++++------------- 3 files changed, 102 insertions(+), 184 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 70b02d60191..cfac14f3011 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -6,6 +6,7 @@ #include "../../types/inc/utils.hpp" using namespace winrt; +using namespace winrt::Microsoft::Terminal; using namespace winrt::Windows::Foundation; using namespace ::Microsoft::Console; @@ -22,7 +23,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Monarch::~Monarch() { - printf("~Monarch()\n"); } uint64_t Monarch::GetPID() @@ -30,27 +30,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _ourPID; } - uint64_t Monarch::AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant) + uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) { - // TODO: This whole algorithm is terrible. There's gotta be a better way + // TODO:projects/5 This is terrible. There's gotta be a better way // of finding the first opening in a non-consecutive map of int->object auto providedID = peasant.GetID(); if (providedID == 0) { + // Peasant doesn't currently have an ID. Assign it a new one. peasant.AssignID(_nextPeasantID++); - printf("Assigned the peasant the ID %lld\n", peasant.GetID()); } else { - printf("Peasant already had an ID, %lld\n", peasant.GetID()); + // Peasant already had an ID (from an older monarch). Leave that one + // be. Make sure that the next peasant's ID is higher than it. _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; } + auto newPeasantsId = peasant.GetID(); _peasants[newPeasantsId] = peasant; - _setMostRecentPeasant(newPeasantsId); - printf("(the next new peasant will get the ID %lld)\n", _nextPeasantID); + // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); return newPeasantsId; @@ -59,14 +60,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/) { - if (auto peasant{ sender.try_as() }) + // TODO:projects/5 Pass the desktop and timestamp of when the window was + // activated in `args`. + + if (auto peasant{ sender.try_as() }) { auto theirID = peasant.GetID(); _setMostRecentPeasant(theirID); } } - winrt::Microsoft::Terminal::Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) + // Method Description: + // - Lookup a peasant by its ID. + // Arguments: + // - peasantID: The ID Of the peasant to find + // Return Value: + // - the peasant if it exists in our map, otherwise null + Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) { auto peasantSearch = _peasants.find(peasantID); return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; @@ -74,8 +84,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Monarch::_setMostRecentPeasant(const uint64_t peasantID) { + // TODO:projects/5 Use a heap/priority queue per-desktop to track which + // peasant was the most recent per-desktop. When we want to get the most + // recent of all desktops (WindowingBehavior::UseExisting), then use the + // most recent of all desktops. _mostRecentPeasant = peasantID; - printf("\x1b[90mThe most recent peasant is now \x1b[m#%llu\n", _mostRecentPeasant); } void Monarch::SetSelfID(const uint64_t selfID) @@ -96,86 +109,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _setMostRecentPeasant(_thisPeasantID); } - bool Monarch::ProposeCommandline(array_view args, - winrt::hstring cwd) + bool Monarch::ProposeCommandline(array_view /*args*/, + winrt::hstring /*cwd*/) { - // auto argsProcessed = 0; - // std::wstring fullCmdline; - // for (const auto& arg : args) - // { - // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - // fullCmdline += L" "; - // } - // wprintf(L"\x1b[36mProposed Commandline\x1b[m: \""); - // wprintf(fullCmdline.c_str()); - // wprintf(L"\"\n"); - - bool createNewWindow = true; - - if (args.size() >= 3) - { - // We'll need three args at least - [WindowsTerminal.exe, -s, - // id] to be able to have a session ID passed on the commandline. - - if (args[1] == L"-w" || args[1] == L"--window") - { - auto sessionId = std::stoi({ args[2].data(), args[2].size() }); - printf("Found a commandline intended for session %d\n", sessionId); - - // TODO:MG - // HACK: do an args[2:] to slice off the `-w window` args. - array_view argsNoWindow{ args.begin() + 2, args.end() }; - if (sessionId < 0) - { - printf("That certainly isn't a valid ID, they should make a new window.\n"); - createNewWindow = true; - } - else if (sessionId == 0) - { - printf("Session 0 is actually #%llu\n", _mostRecentPeasant); - if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) - { - // TODO In the morning: - // Right now, this commandline includes the "-w window" param, and CLI11 is biting it when parsing that. - // Either: - // * hack yank it for the time being (args[2:]) - // * actually have an AppCommandlineArgs do the parsing. - - auto eventArgs = winrt::make_self(argsNoWindow, cwd); - mruPeasant.ExecuteCommandline(*eventArgs); - createNewWindow = false; - } - } - else - { - if (auto otherPeasant = _getPeasant(sessionId)) - { - auto eventArgs = winrt::make_self(argsNoWindow, cwd); - otherPeasant.ExecuteCommandline(*eventArgs); - createNewWindow = false; - } - else - { - printf("I couldn't find a peasant for that ID, they should make a new window.\n"); - } - } - } - } - else if (_windowingBehavior == WindowingBehavior::UseExisting) - { - if (auto mruPeasant = _getPeasant(_mostRecentPeasant)) - { - auto eventArgs = winrt::make_self(args, cwd); - mruPeasant.ExecuteCommandline(*eventArgs); - createNewWindow = false; - } - } - else - { - printf("They definitely weren't an existing process. They should make a new window.\n"); - } - - return createNewWindow; + // TODO:projects/5 + // The branch dev/migrie/f/remote-commandlines has a more complete + // version of this function, with a naive implementation. For now, we + // always want to create a new window, so we'll just return true. This + // will tell the caller that we didn't handle the commandline, and they + // should open a new window to deal with it themself. + return true; } } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 5ba26eada7a..884eab6f26a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -15,9 +15,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowManager::WindowManager() { + // Register with COM as a server for the Monarch class _registerAsMonarch(); + // Instantiate an instance of the Monarch. This may or may not be in-proc! _createMonarch(); } + WindowManager::~WindowManager() { // IMPORTANT! Tear down the registration as soon as we exit. If we're not a @@ -36,22 +39,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // launched with them! // // Otherwise, the King will tell us if we should make a new window - const bool createNewWindow = isKing || - _monarch.ProposeCommandline(args, cwd); + _shouldCreateWindow = isKing || + _monarch.ProposeCommandline(args, cwd); - if (createNewWindow) + if (_shouldCreateWindow) { + // If we should create a new window, then instantiate our Peasant + // instance, and tell that peasant to handle that commandline. _createOurPeasant(); auto eventArgs = winrt::make_self(args, cwd); _peasant.ExecuteCommandline(*eventArgs); - _shouldCreateWindow = true; - } - else - { - // printf("The Monarch instructed us to not create a new window. We'll be exiting now.\n"); - _shouldCreateWindow = false; } + // Othersize, we'll do _nothing_. } bool WindowManager::ShouldCreateWindow() @@ -93,14 +93,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto p = winrt::make_self(); _peasant = *p; - auto ourID = _monarch.AddPeasant(_peasant); - ourID; - // printf("The monarch assigned us the ID %llu\n", ourID); - - // if (areWeTheKing()) - // { - // remindKingWhoTheyAre(*peasant); - // } + _monarch.AddPeasant(_peasant); + + // TODO:MG Spawn a thread to wait on the monarch, and handle the election return _peasant; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0eb9418cd09..aeb5cc739df 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -30,51 +30,17 @@ AppHost::AppHost() noexcept : { _logic = _app.Logic(); // get a ref to app's logic + // If there were commandline args to our process, try and process them here. + // Do this before AppLogic::Create, otherwise this will have no effect. + // + // This will send our commandline to the Monarch, to ask if we should make a + // new window or not. If not, exit immediately. + _HandleCommandlineArgs(); + if (!_shouldCreateWindow) { - std::vector args; - if (auto commandline{ GetCommandLineW() }) - { - int argc = 0; - - // Get the argv, and turn them into a hstring array to pass to the app. - wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - if (argv) - { - for (auto& elem : wil::make_range(argv.get(), argc)) - { - args.emplace_back(elem); - } - } - } - if (args.empty()) - { - args.emplace_back(L"wt.exe"); - } - - _windowManager.ProposeCommandline({ args }, L"placeholder/cwd"); - // _RegisterAsMonarch(); - // _CreateMonarch(); - _shouldCreateWindow = _windowManager.ShouldCreateWindow(); - if (!_shouldCreateWindow) - { - return; - } - if (auto peasant{ _windowManager.CurrentWindow() }) - { - peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); - } - - // TODO:MG if we end up not creating a new window, we crash. I'm - // thinking this is because the XAML host is not happy about being torn - // down before it has a chance to do really anything. Is there some way - // to get the app logic without instantiating the entire app? or at - // least the parts we'll need for remoting? + return; } - // If there were commandline args to our process, try and process them here. - // Do this before AppLogic::Create, otherwise this will have no effect - _HandleCommandlineArgs(); // TODO:MG <-- This probably needs to move into _ProposeCommandlineToMonarch - _useNonClientArea = _logic.GetShowTabsInTitlebar(); if (_useNonClientArea) { @@ -137,9 +103,35 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& } } +void _buildArgsFromCommandline(std::vector& args) +{ + if (auto commandline{ GetCommandLineW() }) + { + int argc = 0; + + // Get the argv, and turn them into a hstring array to pass to the app. + wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; + if (argv) + { + for (auto& elem : wil::make_range(argv.get(), argc)) + { + args.emplace_back(elem); + } + } + } + if (args.empty()) + { + args.emplace_back(L"wt.exe"); + } +} + // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to -// the app logic for processing. +// the WindowManager, to ask if we should become a window process. +// - If we should create a window, then pass the arguements to the app logic for +// processing. +// - If we shouldn't become a window, set _shouldCreateWindow to false and exit +// immediately. // - If the logic determined there's an error while processing that commandline, // display a message box to the user with the text of the error, and exit. // * We display a message box because we're a Win32 application (not a @@ -152,6 +144,17 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& // - void AppHost::_HandleCommandlineArgs() { + std::vector args; + _buildArgsFromCommandline(args); + std::wstring cwd{ wil::GetCurrentDirectoryW() }; + _windowManager.ProposeCommandline({ args }, { cwd }); + + _shouldCreateWindow = _windowManager.ShouldCreateWindow(); + if (!_shouldCreateWindow) + { + return; + } + if (auto peasant{ _windowManager.CurrentWindow() }) { if (auto args{ peasant.InitialArgs() }) @@ -176,42 +179,19 @@ void AppHost::_HandleCommandlineArgs() } } } + + // After handling the initial args, hookup the callback for handling + // future commandline invocations. When our peasant is told to execute a + // commandline (in the future), it'll trigger this callback, that we'll + // use to send the actions to the app. + peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); } - // if (auto commandline{ GetCommandLineW() }) - // { - // int argc = 0; - - // // Get the argv, and turn them into a hstring array to pass to the app. - // wil::unique_any argv{ CommandLineToArgvW(commandline, &argc) }; - // if (argv) - // { - // std::vector args; - // for (auto& elem : wil::make_range(argv.get(), argc)) - // { - // args.emplace_back(elem); - // } - - // const auto result = _logic.SetStartupCommandline({ args }); - // const auto message = _logic.ParseCommandlineMessage(); - // if (!message.empty()) - // { - // const auto displayHelp = result == 0; - // const auto messageTitle = displayHelp ? IDS_HELP_DIALOG_TITLE : IDS_ERROR_DIALOG_TITLE; - // const auto messageIcon = displayHelp ? MB_ICONWARNING : MB_ICONERROR; - // // TODO:GH#4134: polish this dialog more, to make the text more - // // like msiexec /? - // MessageBoxW(nullptr, - // message.data(), - // GetStringResource(messageTitle).data(), - // MB_OK | messageIcon); - - // if (_logic.ShouldExitEarly()) - // { - // ExitProcess(result); - // } - // } - // } - // } + + // TODO:projects/5 if we end up not creating a new window, we crash. I'm + // thinking this is because the XAML host is not happy about being torn + // down before it has a chance to do really anything. Is there some way + // to get the app logic without instantiating the entire app? or at + // least the parts we'll need for remoting? } // Method Description: From 010333198725f21791c4d9ad28a7dc7f990ba816 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 12:49:27 -0600 Subject: [PATCH 13/76] cleanup Peasant for review --- src/cascadia/Remoting/Monarch.cpp | 21 +++------------ src/cascadia/Remoting/Monarch.h | 2 -- src/cascadia/Remoting/Peasant.cpp | 24 +++++------------ src/cascadia/Remoting/Peasant.h | 11 +++++--- .../UnitTests_Remoting/RemotingTests.cpp | 26 +++++++++++++++++++ 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cfac14f3011..5aa35ca425e 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -54,6 +54,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + // TODO:projects/5 Wait on the peasant's PID, and remove them from the + // map if they die. + return newPeasantsId; } @@ -91,24 +94,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mostRecentPeasant = peasantID; } - void Monarch::SetSelfID(const uint64_t selfID) - { - this->_thisPeasantID = selfID; - // Right now, the monarch assumes the role of the most recent - // window. If the monarch dies, and a new monarch takes over, then the - // entire stack of MRU windows will go with it. That's not what you - // want! - // - // In the real app, we'll have each window also track the timestamp it - // was activated at, and the monarch will cache these. So a new monarch - // could re-query these last activated timestamps, and reconstruct the - // MRU stack. - // - // This is a sample though, and we're not too worried about complete - // correctness here. - _setMostRecentPeasant(_thisPeasantID); - } - bool Monarch::ProposeCommandline(array_view /*args*/, winrt::hstring /*cwd*/) { diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index a79cd7754fd..bacea9731fd 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -34,8 +34,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); - void SetSelfID(const uint64_t selfID); - bool ProposeCommandline(array_view args, winrt::hstring cwd); private: diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index cd8b98fe55a..3dbb6e36081 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -11,7 +11,12 @@ using namespace ::Microsoft::Console; namespace winrt::Microsoft::Terminal::Remoting::implementation { - Peasant::Peasant() + Peasant::Peasant() : + _ourPID{ GetCurrentProcessId() } + { + } + Peasant::Peasant(const uint64_t testPID) : + _ourPID{ testPID } { } @@ -26,7 +31,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Peasant::GetPID() { - return GetCurrentProcessId(); + return _ourPID; } bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args) @@ -38,24 +43,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _ExecuteCommandlineRequestedHandlers(*this, args); - // auto argsProcessed = 0; - // std::wstring fullCmdline; - // for (const auto& arg : args) - // { - // fullCmdline += argsProcessed++ == 0 ? L"sample.exe" : arg; - // fullCmdline += L" "; - // } - // wprintf(L"\x1b[32mExecuted Commandline\x1b[m: \""); - // wprintf(fullCmdline.c_str()); - // wprintf(L"\"\n"); return true; } - void Peasant::raiseActivatedEvent() - { - _WindowActivatedHandlers(*this, nullptr); - } - Remoting::CommandlineArgs Peasant::InitialArgs() { return _initialArgs; diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index e2181690e2b..d24d6c11656 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -3,6 +3,10 @@ #include "Peasant.g.h" #include "../cascadia/inc/cppwinrt_utils.h" +namespace RemotingUnitTests +{ + class RemotingTests; +}; namespace winrt::Microsoft::Terminal::Remoting::implementation { struct Peasant : public PeasantT @@ -15,18 +19,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - void raiseActivatedEvent(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); private: + Peasant(const uint64_t testPID); + uint64_t _ourPID; + uint64_t _id{ 0 }; winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; - // array_view _args; - // winrt::hstring _cwd; + friend class RemotingUnitTests::RemotingTests; }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index ac96debf541..3cf4f8d92f8 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -18,6 +18,12 @@ using namespace winrt::Microsoft::Terminal; name.attach(&_local_##name##); \ auto cleanup = wil::scope_exit([&]() { name.detach(); }); +#define MAKE_PEASANT(name, pid) \ + Remoting::implementation::Peasant _local_##name##{ pid }; \ + com_ptr name; \ + name.attach(&_local_##name##); \ + auto cleanup = wil::scope_exit([&]() { name.detach(); }); + namespace RemotingUnitTests { class RemotingTests @@ -26,6 +32,7 @@ namespace RemotingUnitTests END_TEST_CLASS() TEST_METHOD(CreateMonarch); + TEST_METHOD(CreatePeasant); TEST_CLASS_SETUP(ClassSetup) { @@ -52,4 +59,23 @@ namespace RemotingUnitTests L"A Monarch with an explicit PID should use the one we provided"); } + void RemotingTests::CreatePeasant() + { + auto p1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(p1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + p1->GetPID(), + L"A Peasant without an explicit PID should use the current PID"); + + Log::Comment(L"That's what we need for window process management, but for tests, it'll be more useful to fake the PIDs."); + + auto expectedFakePID = 2345u; + MAKE_PEASANT(p2, expectedFakePID); + + VERIFY_IS_NOT_NULL(p2); + VERIFY_ARE_EQUAL(expectedFakePID, + p2->GetPID(), + L"A Peasant with an explicit PID should use the one we provided"); + } + } From b4fe1bffbfed2fecad8f8c61d6a1ee0c3f01efbc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 14:26:06 -0600 Subject: [PATCH 14/76] Final cleanup for review --- src/cascadia/Remoting/Monarch.cpp | 31 +++++++++++++++++-- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/Remoting/Peasant.cpp | 9 ++++++ src/cascadia/Remoting/WindowManager.cpp | 8 ++--- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/Remoting/WindowManager.idl | 5 ++- .../UnitTests_Remoting/RemotingTests.cpp | 10 ++++-- src/cascadia/WindowsTerminal/AppHost.cpp | 5 ++- 9 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 5aa35ca425e..58d29135671 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -16,6 +16,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _ourPID{ GetCurrentProcessId() } { } + + // This is a private constructor to be used in unit tests, where we don't + // want each Monarch to necessarily use the current PID. Monarch::Monarch(const uint64_t testPID) : _ourPID{ testPID } { @@ -30,6 +33,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _ourPID; } + // Method Description: + // - Add the given peasant to the list of peasants we're tracking. This Peasant may have already been assigned an ID. If it hasn't, then give it an ID. + // Arguments: + // - peasant: the new Peasant to track. + // Return Value: + // - the ID assigned to the peasant. uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) { // TODO:projects/5 This is terrible. There's gotta be a better way @@ -55,11 +64,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); // TODO:projects/5 Wait on the peasant's PID, and remove them from the - // map if they die. + // map if they die. This won't work great in tests though, with fake + // PIDs. return newPeasantsId; } + // Method Description: + // - Event handler for the Peasant::WindowActivated event. Used as an + // opportunity for us to update our internal stack of the "most recent + // window". + // Arguments: + // - sender: the Peasant that raised this event. This might be out-of-proc! + // Return Value: + // - void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/) { @@ -94,8 +112,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mostRecentPeasant = peasantID; } - bool Monarch::ProposeCommandline(array_view /*args*/, - winrt::hstring /*cwd*/) + // Method Description: + // - Try to handle a commandline from a new WT invocation. We might need to + // hand the commandline to an existing window, or we might need to tell + // the caller that they need to become a new window to handle it themself. + // Arguments: + // - + // Return Value: + // - + bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& /*args*/) { // TODO:projects/5 // The branch dev/migrie/f/remote-commandlines has a more complete diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index bacea9731fd..5331f753f45 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -34,7 +34,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); - bool ProposeCommandline(array_view args, winrt::hstring cwd); + bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); private: Monarch(const uint64_t testPID); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 8b228480caa..bf89bcccef1 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -7,6 +7,6 @@ namespace Microsoft.Terminal.Remoting UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); - Boolean ProposeCommandline(String[] args, String cwd); + Boolean ProposeCommandline(CommandlineArgs args); }; } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 3dbb6e36081..a0b8d8b372d 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -15,6 +15,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _ourPID{ GetCurrentProcessId() } { } + + // This is a private constructor to be used in unit tests, where we don't + // want each Peasant to necessarily use the current PID. Peasant::Peasant(const uint64_t testPID) : _ourPID{ testPID } { @@ -36,11 +39,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool Peasant::ExecuteCommandline(const Remoting::CommandlineArgs& args) { + // If this is the first set of args we were ever told about, stash them + // away. We'll need to get at them later, when we setup the startup + // actions for the window. if (_initialArgs == nullptr) { _initialArgs = args; } + // Raise an event with these args. The AppHost will listen for this + // event to know when to take these args and dispatch them to a + // currently-running window. _ExecuteCommandlineRequestedHandlers(*this, args); return true; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 884eab6f26a..6f50eadebed 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -31,8 +31,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; } - void WindowManager::ProposeCommandline(array_view args, - const winrt::hstring cwd) + void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) { const bool isKing = _areWeTheKing(); // If we're the king, we _definitely_ want to process the arguments, we were @@ -40,7 +39,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // Otherwise, the King will tell us if we should make a new window _shouldCreateWindow = isKing || - _monarch.ProposeCommandline(args, cwd); + _monarch.ProposeCommandline(args); if (_shouldCreateWindow) { @@ -48,8 +47,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // instance, and tell that peasant to handle that commandline. _createOurPeasant(); - auto eventArgs = winrt::make_self(args, cwd); - _peasant.ExecuteCommandline(*eventArgs); + _peasant.ExecuteCommandline(args); } // Othersize, we'll do _nothing_. } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 1d36ec15bf5..8f60530b455 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -12,7 +12,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager(); ~WindowManager(); - void ProposeCommandline(array_view args, const winrt::hstring cwd); + void ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); bool ShouldCreateWindow(); winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index 55785aac718..e9ef1ee345c 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -3,12 +3,11 @@ import "Peasant.idl"; namespace Microsoft.Terminal.Remoting { - [default_interface] runtimeclass WindowManager { WindowManager(); - void ProposeCommandline(String[] commands, String cwd); + void ProposeCommandline(CommandlineArgs args); Boolean ShouldCreateWindow { get; }; - Peasant CurrentWindow(); + IPeasant CurrentWindow(); }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 3cf4f8d92f8..396482ecc03 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -12,17 +12,23 @@ using namespace WEX::Common; using namespace winrt; using namespace winrt::Microsoft::Terminal; +// These are some gross macros that let us call a private ctor for +// Monarch/Peasant. We can't just use make_self, because that doesn't let us +// call a private ctor. We can use com_ptr::attach, but since we're allocating +// the thing on the stack, we need to make sure to call detach before the object +// is destructed. + #define MAKE_MONARCH(name, pid) \ Remoting::implementation::Monarch _local_##name##{ pid }; \ com_ptr name; \ name.attach(&_local_##name##); \ - auto cleanup = wil::scope_exit([&]() { name.detach(); }); + auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); }); #define MAKE_PEASANT(name, pid) \ Remoting::implementation::Peasant _local_##name##{ pid }; \ com_ptr name; \ name.attach(&_local_##name##); \ - auto cleanup = wil::scope_exit([&]() { name.detach(); }); + auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); }); namespace RemotingUnitTests { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index aeb5cc739df..38c478b40d7 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -14,6 +14,7 @@ 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 winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace ::Microsoft::Console; using namespace ::Microsoft::Console::Types; @@ -147,7 +148,9 @@ void AppHost::_HandleCommandlineArgs() std::vector args; _buildArgsFromCommandline(args); std::wstring cwd{ wil::GetCurrentDirectoryW() }; - _windowManager.ProposeCommandline({ args }, { cwd }); + + Remoting::CommandlineArgs eventArgs{ { args }, { cwd } }; + _windowManager.ProposeCommandline(eventArgs); _shouldCreateWindow = _windowManager.ShouldCreateWindow(); if (!_shouldCreateWindow) From d08e65ce032fba14c24665907336986f8ec1ac7a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 14:33:50 -0600 Subject: [PATCH 15/76] _final_ final cleanup for review --- .../actions/spell-check/dictionary/apis.txt | 1 + build/Helix/runtests.cmd | 2 +- src/cascadia/Remoting/CommandlineArgs.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 21 +------------------ src/cascadia/WindowsTerminal/AppHost.h | 5 ----- 5 files changed, 4 insertions(+), 27 deletions(-) diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index 627ddbf0875..73fe69d0835 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -34,6 +34,7 @@ IExplorer IInheritable IMap IObject +IPeasant IStorage ITab ITaskbar diff --git a/build/Helix/runtests.cmd b/build/Helix/runtests.cmd index 3b155e279a9..b55ee2cf3c8 100644 --- a/build/Helix/runtests.cmd +++ b/build/Helix/runtests.cmd @@ -28,7 +28,7 @@ echo %TIME% powershell -ExecutionPolicy Bypass .\InstallTestAppDependencies.ps1 echo %TIME% -set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Remoting.LocalTests.dll Conhost.UIA.Tests.dll +set testBinaryCandidates=TerminalApp.LocalTests.dll SettingsModel.LocalTests.dll Conhost.UIA.Tests.dll set testBinaries= for %%B in (%testBinaryCandidates%) do ( if exist %%B ( diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index bbe31961c6e..b9830d3d499 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -10,7 +10,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // If you try to move this into the header, you will experience P A I N // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler - // will give you just the most impossible tmplate errors to try and + // will give you just the most impossible template errors to try and // decipher. void CommandlineArgs::Args(winrt::array_view const& value) { diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 38c478b40d7..985ea6b8a81 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -517,26 +517,7 @@ bool AppHost::HasWindow() return _shouldCreateWindow; } -// void AppHost::_RegisterAsMonarch() -// { -// winrt::check_hresult(CoRegisterClassObject(Monarch_clsid, -// winrt::make<::MonarchFactory>().get(), -// CLSCTX_LOCAL_SERVER, -// REGCLS_MULTIPLEUSE, -// &_registrationHostClass)); -// } - -// void AppHost::_CreateMonarch() -// { -// } - -// bool AppHost::_ProposeCommandlineToMonarch() -// { -// // returns true if we should create a new window -// return true; -// } - -void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, +void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) { _logic.ExecuteCommandline(args.Args()); diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 22dbbc2c113..18c175d1757 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -26,7 +26,6 @@ class AppHost winrt::TerminalApp::App _app; winrt::TerminalApp::AppLogic _logic; bool _shouldCreateWindow{ false }; - // DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::WindowManager _windowManager{ nullptr }; void _HandleCommandlineArgs(); @@ -46,10 +45,6 @@ class AppHost const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const til::point coord, const int32_t delta); - // void _RegisterAsMonarch(); - // void _CreateMonarch(); - // bool _ProposeCommandlineToMonarch(); - void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); }; From e101efd11dba93208bba9c4191c937eabbbb4ed3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 07:12:12 -0600 Subject: [PATCH 16/76] pr nits --- src/cascadia/Remoting/CommandlineArgs.cpp | 1 + src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj | 3 --- src/cascadia/Remoting/Monarch.cpp | 4 ++-- src/cascadia/Remoting/WindowManager.cpp | 4 ++-- src/cascadia/Remoting/WindowManager.h | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 3 +-- tools/tests.xml | 2 +- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index b9830d3d499..db90f3381fd 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -8,6 +8,7 @@ using namespace winrt::Windows::Foundation; namespace winrt::Microsoft::Terminal::Remoting::implementation { + // LOAD BEARING CODE // If you try to move this into the header, you will experience P A I N // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler // will give you just the most impossible template errors to try and diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index b5d5355e414..d3f1bf6c7f4 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -82,9 +82,6 @@ pch.h - ..;$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); - - 4702;%(DisableSpecificWarnings) WindowsApp.lib;user32.lib;shell32.lib;%(AdditionalDependencies) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 58d29135671..46e18dab3d4 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -115,7 +115,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Method Description: // - Try to handle a commandline from a new WT invocation. We might need to // hand the commandline to an existing window, or we might need to tell - // the caller that they need to become a new window to handle it themself. + // the caller that they need to become a new window to handle it themselves. // Arguments: // - // Return Value: @@ -127,7 +127,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // version of this function, with a naive implementation. For now, we // always want to create a new window, so we'll just return true. This // will tell the caller that we didn't handle the commandline, and they - // should open a new window to deal with it themself. + // should open a new window to deal with it themselves. return true; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 6f50eadebed..c0a2b87f2fe 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant.ExecuteCommandline(args); } - // Othersize, we'll do _nothing_. + // Otherwise, we'll do _nothing_. } bool WindowManager::ShouldCreateWindow() @@ -74,7 +74,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // definitions for our types automatically. This only works in the following // cases: // - // * If we're running unpackaged: the .winmd but be a sibling of the .exe + // * If we're running unpackaged: the .winmd must be a sibling of the .exe // * If we're running packaged: the .winmd must be in the package root _monarch = create_instance(Monarch_clsid, CLSCTX_LOCAL_SERVER); diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 8f60530b455..f755d0c700a 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -7,7 +7,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { - struct WindowManager : public WindowManagerT + struct WindowManager final : public WindowManagerT { WindowManager(); ~WindowManager(); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 985ea6b8a81..57043b0ed13 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "AppHost.h" -// #include "MonarchFactory.h" #include "../types/inc/Viewport.hpp" #include "../types/inc/utils.hpp" #include "../types/inc/User32Utils.hpp" @@ -129,7 +128,7 @@ void _buildArgsFromCommandline(std::vector& args) // Method Description: // - Retrieve any commandline args passed on the commandline, and pass them to // the WindowManager, to ask if we should become a window process. -// - If we should create a window, then pass the arguements to the app logic for +// - If we should create a window, then pass the arguments to the app logic for // processing. // - If we shouldn't become a window, set _shouldCreateWindow to false and exit // immediately. diff --git a/tools/tests.xml b/tools/tests.xml index 0a296e21f6d..d1831acfad8 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -6,7 +6,7 @@ - + From e1402d834f2dfb6055989b93f4976743c8b34c76 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 09:29:45 -0600 Subject: [PATCH 17/76] This seems to work to create a thread listening for the monarch, and safely tear it down. It _seems_ like it. --- src/cascadia/Remoting/WindowManager.cpp | 81 ++++++++++++++++++++++++- src/cascadia/Remoting/WindowManager.h | 8 ++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index c0a2b87f2fe..50de554bf30 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -15,6 +15,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { WindowManager::WindowManager() { + _monarchWaitInterrupt.create(); + // Register with COM as a server for the Monarch class _registerAsMonarch(); // Instantiate an instance of the Monarch. This may or may not be in-proc! @@ -29,6 +31,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // monarch! CoRevokeClassObject(_registrationHostClass); _registrationHostClass = 0; + _monarchWaitInterrupt.SetEvent(); + if (_electionThread.joinable()) + { + _electionThread.join(); + } } void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) @@ -47,6 +54,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // instance, and tell that peasant to handle that commandline. _createOurPeasant(); + // TODO:MG Spawn a thread to wait on the monarch, and handle the election + if (!isKing) + { + _createPeasantThread(); + } + _peasant.ExecuteCommandline(args); } // Otherwise, we'll do _nothing_. @@ -93,11 +106,75 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant = *p; _monarch.AddPeasant(_peasant); - // TODO:MG Spawn a thread to wait on the monarch, and handle the election - return _peasant; } + bool WindowManager::_electionNight2020() + { + _createMonarch(); + if (_areWeTheKing()) + { + return true; + } + return false; + } + + void WindowManager::_createPeasantThread() + { + // If we catch an exception trying to get at the monarch ever, we can + // set the _monarchWaitInterrupt, and use that to trigger a new + // election. Though, we wouldn't be able to retry the function that + // caused the exception in the first place... + + _electionThread = std::thread([this] { + HANDLE waits[2]; + waits[1] = _monarchWaitInterrupt.get(); + + bool exitRequested = false; + while (!exitRequested) + { + wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast(_monarch.GetPID())) }; + // TODO:MG + // if (hMonarch.get() == nullptr) + // { + // const auto gle = GetLastError(); + // return false; + // } + waits[0] = hMonarch.get(); + auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); + + switch (waitResult) + { + case WAIT_OBJECT_0 + 0: // waits[0] was signaled + // printf("THE KING IS \x1b[31mDEAD\x1b[m\n"); + // // Return false here - this will trigger us to find the new monarch + // return false; + // + // TODO:MG Connect to the new monarch, which might be us! + exitRequested = _electionNight2020(); + break; + case WAIT_OBJECT_0 + 1: // waits[1] was signaled + exitRequested = true; + break; + + case WAIT_TIMEOUT: + printf("Wait timed out. This should be impossible.\n"); + exitRequested = true; + break; + + // Return value is invalid. + default: + { + auto gle = GetLastError(); + printf("WaitForMultipleObjects returned: %d\n", waitResult); + printf("Wait error: %d\n", gle); + ExitProcess(0); + } + } + } + }); + } + Remoting::Peasant WindowManager::CurrentWindow() { return _peasant; diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index f755d0c700a..e51fc79b07b 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -7,7 +7,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { - struct WindowManager final : public WindowManagerT + struct WindowManager final : public WindowManagerT { WindowManager(); ~WindowManager(); @@ -23,10 +23,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; + wil::unique_event _monarchWaitInterrupt; + std::thread _electionThread; + void _registerAsMonarch(); void _createMonarch(); bool _areWeTheKing(); winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); + + bool _electionNight2020(); + void _createPeasantThread(); }; } From f978a9c52cb2b5ca935521e70773fa652d2fb331 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 12:45:29 -0600 Subject: [PATCH 18/76] it's hard to believe he's gone / were it so easy --- src/cascadia/Remoting/Monarch.cpp | 19 +- src/cascadia/Remoting/WindowManager.cpp | 54 ++++- src/cascadia/Remoting/WindowManager.h | 7 + .../UnitTests_Remoting/RemotingTests.cpp | 208 ++++++++++++++++++ 4 files changed, 278 insertions(+), 10 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 46e18dab3d4..d8543360cc7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -66,6 +66,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // TODO:projects/5 Wait on the peasant's PID, and remove them from the // map if they die. This won't work great in tests though, with fake // PIDs. + // + // We should trigger a callback. The manager will use this callback as + // an opportunity to start waiting on the new peasant. return newPeasantsId; } @@ -99,8 +102,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - the peasant if it exists in our map, otherwise null Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID) { - auto peasantSearch = _peasants.find(peasantID); - return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + try + { + auto peasantSearch = _peasants.find(peasantID); + auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + maybeThePeasant.GetPID(); + return maybeThePeasant; + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + // TODO: Remove the peasant from the list of peasants + + return nullptr; + } } void Monarch::_setMostRecentPeasant(const uint64_t peasantID) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 50de554bf30..10bf4831465 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -16,11 +16,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager::WindowManager() { _monarchWaitInterrupt.create(); + // _peasantListenerInterrupt.create(); + + // wil::unique_event peasantListenerInterrupt; + // peasantListenerInterrupt.create(); + // _peasantHandles.emplace_back(std::move(peasantListenerInterrupt)); // Register with COM as a server for the Monarch class _registerAsMonarch(); // Instantiate an instance of the Monarch. This may or may not be in-proc! - _createMonarch(); + _createMonarchAndCallbacks(); } WindowManager::~WindowManager() @@ -32,10 +37,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CoRevokeClassObject(_registrationHostClass); _registrationHostClass = 0; _monarchWaitInterrupt.SetEvent(); + // _peasantListenerInterrupt.SetEvent(); if (_electionThread.joinable()) { _electionThread.join(); } + // if (_peasantListenerThread.joinable()) + // { + // _peasantListenerThread.join(); + // } } void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) @@ -93,6 +103,35 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CLSCTX_LOCAL_SERVER); } + void WindowManager::_createMonarchAndCallbacks() + { + _createMonarch(); + const auto isKing = _areWeTheKing(); + if (!isKing) + { + return; + } + // Here, we're the king! + + // TODO:MG Add an even handler to the monarch's PeasantAdded event. + // We'll use that callback as a chance to start waiting on the peasant's + // PID. If they die, we'll tell the monarch to remove them from the + // list. + // _peasantHandles.emplace_back(_peasantListenerInterrupt.get()); + + // _peasantListenerThread = std::thread([this]() { + + // bool exitRequested = false; + // while (!exitRequested) + // { + // } + // }); + + // Wait, don't. Let's just have the monarch try/catch any accesses to + // peasants. If the peasant dies, then it can't get the peasant's + // anything. In that case, _remove it_. + } + bool WindowManager::_areWeTheKing() { auto kingPID = _monarch.GetPID(); @@ -111,7 +150,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_electionNight2020() { - _createMonarch(); + _createMonarchAndCallbacks(); if (_areWeTheKing()) { return true; @@ -134,7 +173,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation while (!exitRequested) { wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast(_monarch.GetPID())) }; - // TODO:MG + // TODO:MG If we fail to open the monarch, then they don't exist + // anymore! Go straight to an election. + // // if (hMonarch.get() == nullptr) // { // const auto gle = GetLastError(); @@ -146,11 +187,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation switch (waitResult) { case WAIT_OBJECT_0 + 0: // waits[0] was signaled - // printf("THE KING IS \x1b[31mDEAD\x1b[m\n"); - // // Return false here - this will trigger us to find the new monarch - // return false; - // - // TODO:MG Connect to the new monarch, which might be us! + // Connect to the new monarch, which might be us! + // If we become the monarch, then we'll return true and exit this thread. exitRequested = _electionNight2020(); break; case WAIT_OBJECT_0 + 1: // waits[1] was signaled diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index e51fc79b07b..acbca463588 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -24,10 +24,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; wil::unique_event _monarchWaitInterrupt; + // wil::unique_event _peasantListenerInterrupt; + std::thread _electionThread; + // std::thread _peasantListenerThread; + + // // NON-OWNING HANDLES + // std::vector _peasantHandles{}; void _registerAsMonarch(); void _createMonarch(); + void _createMonarchAndCallbacks(); bool _areWeTheKing(); winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 396482ecc03..c23e2219555 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -32,6 +32,22 @@ using namespace winrt::Microsoft::Terminal; namespace RemotingUnitTests { + // This is a silly helper struct. + // It will always throw an hresult_error on any of it's methods. + // In the tests, it's hard to emulate a peasant process being totally dead once the Monarch has captured a reference to it. Since everything's in-proc in the tests, we can't decrement the refcount in such a way that the monarch's reference will throw a catchable exception. + // Instead, this class can be used to replace a peasant inside a Monarch, to emulate that peasant process dying. Any time the monarch tries to do something to this peasant, it'll throw an exception. + struct DeadPeasant : implements + { + DeadPeasant() = default; + void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; }; + uint64_t GetID() { throw winrt::hresult_error{}; }; + uint64_t GetPID() { throw winrt::hresult_error{}; }; + bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } + winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); + }; + class RemotingTests { BEGIN_TEST_CLASS(RemotingTests) @@ -39,13 +55,34 @@ namespace RemotingUnitTests TEST_METHOD(CreateMonarch); TEST_METHOD(CreatePeasant); + TEST_METHOD(CreatePeasantWithNew); + TEST_METHOD(AddPeasants); + TEST_METHOD(GetPeasantsByID); + TEST_METHOD(AddPeasantsToNewMonarch); + TEST_METHOD(RemovePeasantFromMonarchWhenFreed); TEST_CLASS_SETUP(ClassSetup) { return true; } + + static void _killPeasant(const com_ptr& m, + const uint64_t peasantID); }; + void RemotingTests::_killPeasant(const com_ptr& m, + const uint64_t peasantID) + { + if (peasantID <= 0) + { + return; + } + + com_ptr tombstone; + tombstone.attach(new DeadPeasant()); + m->_peasants[peasantID] = *tombstone; + } + void RemotingTests::CreateMonarch() { auto m1 = winrt::make_self(); @@ -84,4 +121,175 @@ namespace RemotingUnitTests L"A Peasant with an explicit PID should use the one we provided"); } + void RemotingTests::CreatePeasantWithNew() + { + Log::Comment(L"The same thing as the above test, but with `new` instead of insanity on the stack"); + + auto p1 = winrt::make_self(); + VERIFY_IS_NOT_NULL(p1); + VERIFY_ARE_EQUAL(GetCurrentProcessId(), + p1->GetPID(), + L"A Peasant without an explicit PID should use the current PID"); + + auto expectedFakePID = 2345u; + + com_ptr p2; + VERIFY_IS_NULL(p2); + p2.attach(new Remoting::implementation::Peasant(expectedFakePID)); + + VERIFY_IS_NOT_NULL(p2); + VERIFY_ARE_EQUAL(expectedFakePID, + p2->GetPID(), + L"A Peasant with an explicit PID should use the one we provided"); + } + + void RemotingTests::AddPeasants() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + } + + void RemotingTests::GetPeasantsByID() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + + auto maybeP1 = m0->_getPeasant(1); + VERIFY_IS_NOT_NULL(maybeP1); + VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID()); + + auto maybeP2 = m0->_getPeasant(2); + VERIFY_IS_NOT_NULL(maybeP2); + VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID()); + } + + void RemotingTests::AddPeasantsToNewMonarch() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + const auto monarch3PID = 45678u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + com_ptr m3; + m3.attach(new Remoting::implementation::Monarch(monarch3PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + VERIFY_IS_NOT_NULL(m3); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + + m3->AddPeasant(*p1); + m3->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + } + + void RemotingTests::RemovePeasantFromMonarchWhenFreed() + { + const auto monarch0PID = 12345u; + const auto peasant1PID = 23456u; + const auto peasant2PID = 34567u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + + VERIFY_IS_NOT_NULL(m0); + VERIFY_IS_NOT_NULL(p1); + VERIFY_IS_NOT_NULL(p2); + + VERIFY_ARE_EQUAL(0, p1->GetID()); + VERIFY_ARE_EQUAL(0, p2->GetID()); + + m0->AddPeasant(*p1); + m0->AddPeasant(*p2); + + VERIFY_ARE_EQUAL(1, p1->GetID()); + VERIFY_ARE_EQUAL(2, p2->GetID()); + + VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); + + RemotingTests::_killPeasant(m0, p1->GetID()); + + auto maybeP2 = m0->_getPeasant(2); + VERIFY_IS_NOT_NULL(maybeP2); + VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID()); + + auto maybeP1 = m0->_getPeasant(1); + // VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID()); + VERIFY_IS_NULL(maybeP1); + + VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); + } + } From 5939636182e1d433d18c767e42af8045d4b0490b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 12:48:23 -0600 Subject: [PATCH 19/76] Oh yea actually remove the thing --- src/cascadia/Remoting/Monarch.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index d8543360cc7..9792f5f1ec6 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -106,14 +106,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto peasantSearch = _peasants.find(peasantID); auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; - maybeThePeasant.GetPID(); + if (maybeThePeasant) + { + maybeThePeasant.GetPID(); + } return maybeThePeasant; } catch (...) { LOG_CAUGHT_EXCEPTION(); // TODO: Remove the peasant from the list of peasants - + _peasants.erase(peasantID); return nullptr; } } From c08889585bf3fdb05093b2713ef00402835fb51d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 18 Dec 2020 15:21:53 -0600 Subject: [PATCH 20/76] last commit before the holidays --- src/cascadia/Remoting/Monarch.cpp | 9 +- src/cascadia/Remoting/WindowManager.cpp | 124 ++++++++---------- src/cascadia/Remoting/WindowManager.h | 6 +- .../UnitTests_Remoting/RemotingTests.cpp | 2 +- 4 files changed, 61 insertions(+), 80 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 9792f5f1ec6..591b5d177c4 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -63,13 +63,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); - // TODO:projects/5 Wait on the peasant's PID, and remove them from the - // map if they die. This won't work great in tests though, with fake - // PIDs. - // - // We should trigger a callback. The manager will use this callback as - // an opportunity to start waiting on the new peasant. - return newPeasantsId; } @@ -115,7 +108,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation catch (...) { LOG_CAUGHT_EXCEPTION(); - // TODO: Remove the peasant from the list of peasants + // Remove the peasant from the list of peasants _peasants.erase(peasantID); return nullptr; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 10bf4831465..cb8c124b52a 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -16,11 +16,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation WindowManager::WindowManager() { _monarchWaitInterrupt.create(); - // _peasantListenerInterrupt.create(); - - // wil::unique_event peasantListenerInterrupt; - // peasantListenerInterrupt.create(); - // _peasantHandles.emplace_back(std::move(peasantListenerInterrupt)); // Register with COM as a server for the Monarch class _registerAsMonarch(); @@ -37,15 +32,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CoRevokeClassObject(_registrationHostClass); _registrationHostClass = 0; _monarchWaitInterrupt.SetEvent(); - // _peasantListenerInterrupt.SetEvent(); + if (_electionThread.joinable()) { _electionThread.join(); } - // if (_peasantListenerThread.joinable()) - // { - // _peasantListenerThread.join(); - // } } void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) @@ -113,20 +104,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } // Here, we're the king! - // TODO:MG Add an even handler to the monarch's PeasantAdded event. - // We'll use that callback as a chance to start waiting on the peasant's - // PID. If they die, we'll tell the monarch to remove them from the - // list. - // _peasantHandles.emplace_back(_peasantListenerInterrupt.get()); - - // _peasantListenerThread = std::thread([this]() { - - // bool exitRequested = false; - // while (!exitRequested) - // { - // } - // }); - // Wait, don't. Let's just have the monarch try/catch any accesses to // peasants. If the peasant dies, then it can't get the peasant's // anything. In that case, _remove it_. @@ -151,6 +128,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_electionNight2020() { _createMonarchAndCallbacks(); + + // Tell the new monarch who we are. We might be that monarch! + _monarch.AddPeasant(_peasant); + if (_areWeTheKing()) { return true; @@ -166,51 +147,62 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // caused the exception in the first place... _electionThread = std::thread([this] { - HANDLE waits[2]; - waits[1] = _monarchWaitInterrupt.get(); + _waitOnMonarchThread(); + }); + } + + void WindowManager::_waitOnMonarchThread() + { + HANDLE waits[2]; + waits[1] = _monarchWaitInterrupt.get(); - bool exitRequested = false; - while (!exitRequested) + bool exitRequested = false; + while (!exitRequested) + { + wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, + FALSE, + static_cast(_monarch.GetPID())) }; + // TODO:MG If we fail to open the monarch, then they don't exist + // anymore! Go straight to an election. + // + // TODO:MG At any point in all this, the current monarch might die. + // We go straight to a new election, right? Worst case, eventually, + // we'll become the new monarch. + // + // if (hMonarch.get() == nullptr) + // { + // const auto gle = GetLastError(); + // return false; + // } + waits[0] = hMonarch.get(); + auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); + + switch (waitResult) { - wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast(_monarch.GetPID())) }; - // TODO:MG If we fail to open the monarch, then they don't exist - // anymore! Go straight to an election. - // - // if (hMonarch.get() == nullptr) - // { - // const auto gle = GetLastError(); - // return false; - // } - waits[0] = hMonarch.get(); - auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); - - switch (waitResult) - { - case WAIT_OBJECT_0 + 0: // waits[0] was signaled - // Connect to the new monarch, which might be us! - // If we become the monarch, then we'll return true and exit this thread. - exitRequested = _electionNight2020(); - break; - case WAIT_OBJECT_0 + 1: // waits[1] was signaled - exitRequested = true; - break; - - case WAIT_TIMEOUT: - printf("Wait timed out. This should be impossible.\n"); - exitRequested = true; - break; - - // Return value is invalid. - default: - { - auto gle = GetLastError(); - printf("WaitForMultipleObjects returned: %d\n", waitResult); - printf("Wait error: %d\n", gle); - ExitProcess(0); - } - } + case WAIT_OBJECT_0 + 0: // waits[0] was signaled + // Connect to the new monarch, which might be us! + // If we become the monarch, then we'll return true and exit this thread. + exitRequested = _electionNight2020(); + break; + case WAIT_OBJECT_0 + 1: // waits[1] was signaled + exitRequested = true; + break; + + case WAIT_TIMEOUT: + printf("Wait timed out. This should be impossible.\n"); + exitRequested = true; + break; + + // Return value is invalid. + default: + { + auto gle = GetLastError(); + printf("WaitForMultipleObjects returned: %d\n", waitResult); + printf("Wait error: %d\n", gle); + ExitProcess(0); } - }); + } + } } Remoting::Peasant WindowManager::CurrentWindow() diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index acbca463588..69c26175902 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -24,13 +24,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; wil::unique_event _monarchWaitInterrupt; - // wil::unique_event _peasantListenerInterrupt; std::thread _electionThread; - // std::thread _peasantListenerThread; - - // // NON-OWNING HANDLES - // std::vector _peasantHandles{}; void _registerAsMonarch(); void _createMonarch(); @@ -40,6 +35,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _electionNight2020(); void _createPeasantThread(); + void _waitOnMonarchThread(); }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index c23e2219555..dd3b5ad123d 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -279,6 +279,7 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(2u, m0->_peasants.size()); + Log::Comment(L"Kill peasant 1. Make sure that it gets removed from the monarch."); RemotingTests::_killPeasant(m0, p1->GetID()); auto maybeP2 = m0->_getPeasant(2); @@ -286,7 +287,6 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID()); auto maybeP1 = m0->_getPeasant(1); - // VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID()); VERIFY_IS_NULL(maybeP1); VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); From 0f5c24f661b356ad754b2700bd55a9fa9cb8b09a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 5 Jan 2021 11:22:23 -0600 Subject: [PATCH 21/76] Enable audit mode --- OpenConsole.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenConsole.sln b/OpenConsole.sln index 06bfcb492b7..eaf51110a64 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -2157,6 +2157,7 @@ Global {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.ActiveCfg = Release|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.Build.0 = AuditMode|x64 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.Build.0 = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|Any CPU.ActiveCfg = Debug|Win32 From 921d91582b24ec4afedea45ee715a1aa6fb99ed1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 5 Jan 2021 11:28:10 -0600 Subject: [PATCH 22/76] nits --- .github/actions/spell-check/dictionary/apis.txt | 2 ++ src/cascadia/Remoting/Monarch.cpp | 5 ++++- src/cascadia/Remoting/Monarch.h | 3 +++ src/cascadia/Remoting/Monarch.idl | 3 +++ src/cascadia/Remoting/MonarchFactory.h | 6 +++--- src/cascadia/Remoting/Peasant.cpp | 3 +++ src/cascadia/Remoting/Peasant.h | 3 +++ src/cascadia/Remoting/Peasant.idl | 3 ++- src/cascadia/Remoting/WindowManager.cpp | 11 +++++++---- src/cascadia/Remoting/WindowManager.h | 5 ++++- .../Remoting/dll/Microsoft.Terminal.Remoting.vcxproj | 2 +- 11 files changed, 35 insertions(+), 11 deletions(-) diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index 73fe69d0835..4bb2d3daf84 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -44,6 +44,7 @@ localtime lround LSHIFT msappx +MULTIPLEUSE NCHITTEST NCLBUTTONDBLCLK NCRBUTTONDBLCLK @@ -57,6 +58,7 @@ otms OUTLINETEXTMETRICW overridable PAGESCROLL +REGCLS RETURNCMD rfind roundf diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 46e18dab3d4..ab32df78283 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "Monarch.h" #include "CommandlineArgs.h" @@ -43,7 +46,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // TODO:projects/5 This is terrible. There's gotta be a better way // of finding the first opening in a non-consecutive map of int->object - auto providedID = peasant.GetID(); + const auto providedID = peasant.GetID(); if (providedID == 0) { diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 5331f753f45..374b30ec391 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #pragma once #include "Monarch.g.h" diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index bf89bcccef1..8dcb7a66a7b 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + import "Peasant.idl"; namespace Microsoft.Terminal.Remoting diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h index 9b912995681..5e9add11a3c 100644 --- a/src/cascadia/Remoting/MonarchFactory.h +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "Monarch.h" @@ -8,9 +11,6 @@ // per-process. Once the first monarch is created, we'll stash it in g_weak. // Future callers who try to instantiate a Monarch will get the one that's // already been made. -// -// I'm sure there's a better way to do this with WRL, but I'm not familiar -// enough with WRL to know for sure. winrt::weak_ref g_weak{ nullptr }; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index a0b8d8b372d..ce3c4e96e97 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "Peasant.h" #include "CommandlineArgs.h" diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index d24d6c11656..b83011e8673 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #pragma once #include "Peasant.g.h" diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 7ffb9512170..8597b2489dd 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -1,4 +1,5 @@ - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. namespace Microsoft.Terminal.Remoting { diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index c0a2b87f2fe..f80f8e84cf9 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #include "pch.h" #include "WindowManager.h" #include "MonarchFactory.h" @@ -69,7 +72,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void WindowManager::_createMonarch() { // Heads up! This only works because we're using - // "metadata-based-marshalling" for our WinRT types. THat means the OS is + // "metadata-based-marshalling" for our WinRT types. That means the OS is // using the .winmd file we generate to figure out the proxy/stub // definitions for our types automatically. This only works in the following // cases: @@ -82,8 +85,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_areWeTheKing() { - auto kingPID = _monarch.GetPID(); - auto ourPID = GetCurrentProcessId(); + const auto kingPID{ _monarch.GetPID() }; + const auto ourPID{ GetCurrentProcessId() }; return (ourPID == kingPID); } @@ -93,7 +96,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant = *p; _monarch.AddPeasant(_peasant); - // TODO:MG Spawn a thread to wait on the monarch, and handle the election + // TODO:projects/5 Spawn a thread to wait on the monarch, and handle the election return _peasant; } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index f755d0c700a..0d25a4dfb96 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + #pragma once #include "WindowManager.g.h" @@ -7,7 +10,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { - struct WindowManager final : public WindowManagerT + struct WindowManager final : public WindowManagerT { WindowManager(); ~WindowManager(); diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj index 51c6f62ad18..8b58ce05673 100644 --- a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -61,7 +61,7 @@ User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) - + /INCLUDE:_DllMain@12 /INCLUDE:DllMain From 658db6b568374c068161e17b8db10679c0ee2084 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 6 Jan 2021 07:10:54 -0600 Subject: [PATCH 23/76] Ask the TerminalApp to parse the commandline, and tell us what the window should be. It just always says 0 for now, but in the future it could actually give us useful info. --- .../Remoting/FindTargetWindowArgs.cpp | 5 ++ src/cascadia/Remoting/FindTargetWindowArgs.h | 17 ++++++ .../Microsoft.Terminal.RemotingLib.vcxproj | 6 ++ src/cascadia/Remoting/Monarch.cpp | 56 ++++++++++++++++++- src/cascadia/Remoting/Monarch.h | 3 + src/cascadia/Remoting/Monarch.idl | 8 +++ src/cascadia/Remoting/WindowManager.cpp | 21 +++++++ src/cascadia/Remoting/WindowManager.h | 6 ++ src/cascadia/Remoting/WindowManager.idl | 2 + src/cascadia/TerminalApp/AppLogic.cpp | 20 +++++++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 2 + src/cascadia/WindowsTerminal/AppHost.cpp | 13 +++++ src/cascadia/WindowsTerminal/AppHost.h | 3 + 14 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 src/cascadia/Remoting/FindTargetWindowArgs.cpp create mode 100644 src/cascadia/Remoting/FindTargetWindowArgs.h diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.cpp b/src/cascadia/Remoting/FindTargetWindowArgs.cpp new file mode 100644 index 00000000000..1781c1c9c12 --- /dev/null +++ b/src/cascadia/Remoting/FindTargetWindowArgs.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "FindTargetWindowArgs.h" +#include "FindTargetWindowArgs.g.cpp" diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.h b/src/cascadia/Remoting/FindTargetWindowArgs.h new file mode 100644 index 00000000000..7a30b7b1815 --- /dev/null +++ b/src/cascadia/Remoting/FindTargetWindowArgs.h @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "FindTargetWindowArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct FindTargetWindowArgs : public FindTargetWindowArgsT + { + public: + GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr); + GETSET_PROPERTY(int, ResultTargetWindow, -1); + }; +} diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index d3f1bf6c7f4..0d977767f27 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -19,6 +19,9 @@ Monarch.idl + + Monarch.idl + @@ -36,6 +39,9 @@ Monarch.idl + + Monarch.idl + Create diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 50b6c24f11b..4f31609ebbb 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include "Monarch.h" #include "CommandlineArgs.h" +#include "FindTargetWindowArgs.h" #include "Monarch.g.cpp" #include "../../types/inc/utils.hpp" @@ -117,6 +118,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } + // TODO:MG This probably shouldn't be a public function. I'm making it + // public so the WindowManager can use it to manually tell the monarch it's + // own ID to use as the MRU, when the monarch is first instantiated. THat's + // dumb, but it's a hack to get something working. + // + // That was stupid. I knew it would be but yea it didn't work. + // THe Window manager doesn't have a peasant yet when it first creates the monarch. void Monarch::_setMostRecentPeasant(const uint64_t peasantID) { // TODO:projects/5 Use a heap/priority queue per-desktop to track which @@ -126,6 +134,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mostRecentPeasant = peasantID; } + uint64_t Monarch::_getMostRecentPeasantID() + { + if (_mostRecentPeasant == 0) + { + // We haven't yet been told the MRU peasant. Just use the first one. + // TODO: GOD this is just gonna be a random one. Hacks on hacks on hacks + if (_peasants.size() > 0) + { + return _peasants.begin()->second.GetID(); + } + return 0; + } + else + { + return _mostRecentPeasant; + } + } + // Method Description: // - Try to handle a commandline from a new WT invocation. We might need to // hand the commandline to an existing window, or we might need to tell @@ -133,8 +159,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Arguments: // - // Return Value: - // - - bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& /*args*/) + // - true if the caller should create a new window for this commandline. + // False otherwise - the monarch should have dispatched this commandline + // to another window in this case. + bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& args) { // TODO:projects/5 // The branch dev/migrie/f/remote-commandlines has a more complete @@ -142,6 +170,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // always want to create a new window, so we'll just return true. This // will tell the caller that we didn't handle the commandline, and they // should open a new window to deal with it themselves. + auto findWindowArgs = winrt::make_self(); + findWindowArgs->Args(args); + _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); + const auto targetWindow = findWindowArgs->ResultTargetWindow(); + + // TODO:projects/5 targetWindow==0 -> use the currently active window + if (targetWindow >= 0) + { + uint64_t windowID = ::base::saturated_cast(targetWindow); + + if (windowID == 0) + { + windowID = _getMostRecentPeasantID(); + } + + if (auto targetPeasant{ _getPeasant(windowID) }) + { + targetPeasant.ExecuteCommandline(args); + return false; + } + } + // TEMPORARY: if the target window is -1, then we want a new window. All + // other cases, just do it in this window (for now). + // return targetWindow == -1; return true; } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 374b30ec391..3f58f6916d6 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -39,6 +39,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + private: Monarch(const uint64_t testPID); uint64_t _ourPID; @@ -51,6 +53,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); void _setMostRecentPeasant(const uint64_t peasantID); + uint64_t _getMostRecentPeasantID(); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 8dcb7a66a7b..946b3f63c36 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -5,11 +5,19 @@ import "Peasant.idl"; namespace Microsoft.Terminal.Remoting { + + [default_interface] runtimeclass FindTargetWindowArgs { + CommandlineArgs Args; + Int32 ResultTargetWindow; + } + [default_interface] runtimeclass Monarch { Monarch(); UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); Boolean ProposeCommandline(CommandlineArgs args); + + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index d6d2d07af55..2780c6f03a1 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -106,10 +106,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return; } // Here, we're the king! + // + // This is where you should do any aditional setup that might need to be + // done when we become the king. THis will be called both for the first + // window, and when the current monarch diesd. // Wait, don't. Let's just have the monarch try/catch any accesses to // peasants. If the peasant dies, then it can't get the peasant's // anything. In that case, _remove it_. + + _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); + + // winrt::com_ptr monarchImpl; + // monarchImpl.copy_from(winrt::get_self(_monarch)); + // if (monarchImpl) + // { + // monarchImpl->SetMostRecentPeasant(_peasant.GetID()); + // } } bool WindowManager::_areWeTheKing() @@ -137,6 +150,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_areWeTheKing()) { + // This is only called when a _new_ monarch is elected. We need to + // do this _always_, even on the first instance, which won't have an + // election return true; } return false; @@ -213,4 +229,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _peasant; } + void WindowManager::_raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + { + _FindTargetWindowRequestedHandlers(sender, args); + } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 35f38b51624..b87ce94d646 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -20,6 +20,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); + // Don't do this, the monarch can and will change over time + // FORWARDED_TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs, _monarch, FindTargetWindowRequested); + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); + private: bool _shouldCreateWindow{ false }; DWORD _registrationHostClass{ 0 }; @@ -39,6 +43,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _electionNight2020(); void _createPeasantThread(); void _waitOnMonarchThread(); + void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; } diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index e9ef1ee345c..3099f1128b5 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -1,4 +1,5 @@ import "Peasant.idl"; +import "Monarch.idl"; namespace Microsoft.Terminal.Remoting @@ -9,5 +10,6 @@ namespace Microsoft.Terminal.Remoting void ProposeCommandline(CommandlineArgs args); Boolean ShouldCreateWindow { get; }; IPeasant CurrentWindow(); + event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 12a6223dce1..551fcf0b1a4 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1125,6 +1125,26 @@ namespace winrt::TerminalApp::implementation return result; // TODO:MG does a return value make sense } + int32_t AppLogic::FindTargetWindow(array_view args) + { + ::TerminalApp::AppCommandlineArgs appArgs; + auto result = appArgs.ParseArgs(args); + if (result == 0) + { + // TODO:MG Right now, any successful parse will end up in the same window + return 0; + // TODO:projects/5 We'll want to use the windowingBehavior setting to determine + // well + // Maybe that'd be a special return value out of here, to tell the monarch to do something special + // -1 -> create a new window + // -2 -> find the mru, this desktop + // -3 -> MRU, any desktop + } + + // Any unsuccessful parse will be a new window. + return -1; + } + // Method Description: // - If there were any errors parsing the commandline that was used to // initialize the terminal, this will return a string containing that diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 1a93abcf509..d0b39a73f57 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -30,6 +30,7 @@ namespace winrt::TerminalApp::implementation int32_t SetStartupCommandline(array_view actions); int32_t ExecuteCommandline(array_view actions); + int32_t FindTargetWindow(array_view actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 3abf62eb12a..db50878a225 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -56,6 +56,8 @@ namespace TerminalApp UInt64 GetLastActiveControlTaskbarState(); UInt64 GetLastActiveControlTaskbarProgress(); + Int32 FindTargetWindow(String[] args); + // See IDialogPresenter and TerminalPage's DialogPresenter for more // information. Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 57043b0ed13..bd409c3c52f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -30,6 +30,12 @@ AppHost::AppHost() noexcept : { _logic = _app.Logic(); // get a ref to app's logic + // Inform the WindowManager that it can use us to find the target window for + // a set of commandline args. This needs to be done before + // _HandleCommandlineArgs, because WE might end up being the monarch. That + // would mean we'd need to be responsible for looking that up. + _windowManager.FindTargetWindowRequested({ this, &AppHost::_FindTargetWindow }); + // If there were commandline args to our process, try and process them here. // Do this before AppLogic::Create, otherwise this will have no effect. // @@ -521,3 +527,10 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se { _logic.ExecuteCommandline(args.Args()); } + +void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) +{ + const auto targetWindow = _logic.FindTargetWindow(args.Args().Args()); + args.ResultTargetWindow(targetWindow); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 18c175d1757..4a8dfa5d8e8 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -47,4 +47,7 @@ class AppHost void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); + + void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; From 977db464fabd8d7d4d5cf5afc19ad9bea1e36e10 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 6 Jan 2021 09:50:00 -0600 Subject: [PATCH 24/76] Holy bajesus, this works like a charm --- .../CascadiaPackage/CascadiaPackage.wapproj | 1 + src/cascadia/TerminalApp/AppCommandlineArgs.cpp | 17 +++++++++++++++++ src/cascadia/TerminalApp/AppCommandlineArgs.h | 4 ++++ src/cascadia/TerminalApp/AppLogic.cpp | 4 +++- .../TerminalApp/Resources/en-US/Resources.resw | 3 +++ src/cascadia/WindowsTerminal/main.cpp | 3 ++- 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 3d918935add..dd88eb674f7 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -5,6 +5,7 @@ Native + win 10.0.18362.0 10.0.18362.0 diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f45247b2696..cf6392604ff 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -132,6 +132,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // recent of all desktops (WindowingBehavior::UseExisting), then use the // most recent of all desktops. _mostRecentPeasant = peasantID; + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_MostRecentPeasantSet", + TraceLoggingUInt64(peasantID, "peasantID", "the ID of the activated peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } uint64_t Monarch::_getMostRecentPeasantID() @@ -194,6 +199,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // TODO:MG if the targeted peasant fails to execute the // commandline, we should create our own window to display the // message box. + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline_Existing", + TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return false; } else @@ -205,9 +217,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // `Monarch::ProposeCommandline` needs to return a structure of // `{ shouldCreateWindow: bool, givenID: optional }` // + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline_Existing", + TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } } + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline_NewWindow", + TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // TODO:MG in this case, no usable ID was provided. Return { true, nullopt } return true; } diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index de5a484357b..a64373808c1 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -69,6 +69,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // desktop into this method. The Peasant shouldn't need to be able to // figure it out, but it will need to report it to the monarch. + bool successfullyNotified = false; // Raise our WindowActivated event, to let the monarch know we've been // activated. try @@ -78,6 +79,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // will throw and exception. Just eat it, the election thread will // handle hooking up the new one. _WindowActivatedHandlers(*this, nullptr); + successfullyNotified = true; } catch (...) { @@ -85,6 +87,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation LOG_CAUGHT_EXCEPTION(); } + TraceLoggingWrite(g_hRemotingProvider, + "Peasant_ActivateWindow", + TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), + TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // TODO:MG Open three windows, close the first (the monarch). The focus // should automatically move to the third, from the windows shell. In // that window, `wt -w 0` does not work right. diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index f0b34d04e75..d7428782d07 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -110,6 +110,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { _createMonarch(); const auto isKing = _areWeTheKing(); + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ConnectedToMonarch", + TraceLoggingUInt64(_monarch.GetPID(), "monarchPID", "The PID of the new Monarch"), + TraceLoggingBoolean(isKing, "isKing", "true if we are the new monarch"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + if (!isKing) { return; @@ -147,6 +154,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _peasant = *p; _monarch.AddPeasant(_peasant); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_CreateOurPeasant", + TraceLoggingUInt64(_peasant.GetID(), "peasantID", "The ID of our new peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return _peasant; } @@ -209,11 +221,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation switch (waitResult) { case WAIT_OBJECT_0 + 0: // waits[0] was signaled + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchDied", + TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // Connect to the new monarch, which might be us! // If we become the monarch, then we'll return true and exit this thread. exitRequested = _electionNight2020(); break; case WAIT_OBJECT_0 + 1: // waits[1] was signaled + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchWaitInterrupted", + TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + exitRequested = true; break; diff --git a/src/cascadia/Remoting/init.cpp b/src/cascadia/Remoting/init.cpp new file mode 100644 index 00000000000..51496a8f71c --- /dev/null +++ b/src/cascadia/Remoting/init.cpp @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// Licensed under the MIT license. + +#include "pch.h" +#include +#include + +// Note: Generate GUID using TlgGuid.exe tool +#pragma warning(suppress : 26477) // One of the macros uses 0/NULL. We don't have control to make it nullptr. +TRACELOGGING_DEFINE_PROVIDER( + g_hRemotingProvider, + "Microsoft.Windows.Terminal.Remoting", + // {d6f04aad-629f-539a-77c1-73f5c3e4aa7b} + (0xd6f04aad, 0x629f, 0x539a, 0x77, 0xc1, 0x73, 0xf5, 0xc3, 0xe4, 0xaa, 0x7b), + TraceLoggingOptionMicrosoftTelemetry()); + +BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDll); + TraceLoggingRegister(g_hRemotingProvider); + Microsoft::Console::ErrorReporting::EnableFallbackFailureReporting(g_hRemotingProvider); + break; + case DLL_PROCESS_DETACH: + if (g_hRemotingProvider) + { + TraceLoggingUnregister(g_hRemotingProvider); + } + break; + } + + return TRUE; +} + +UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"Microsoft.Terminal.Remoting/Resources"); diff --git a/src/cascadia/Remoting/pch.h b/src/cascadia/Remoting/pch.h index 165aefe02d8..0fef7307ae5 100644 --- a/src/cascadia/Remoting/pch.h +++ b/src/cascadia/Remoting/pch.h @@ -38,7 +38,7 @@ // Including TraceLogging essentials for the binary #include #include -TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider); +TRACELOGGING_DECLARE_PROVIDER(g_hRemotingProvider); #include #include From 65376865b53969939cedcf23596229cf915177e6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 05:37:25 -0600 Subject: [PATCH 29/76] The peasant will now correctly inform the monarch when it re-connects --- .../Microsoft.Terminal.RemotingLib.vcxproj | 6 ++++ src/cascadia/Remoting/Monarch.cpp | 31 ++++++++++++------- src/cascadia/Remoting/Monarch.h | 4 +-- src/cascadia/Remoting/Monarch.idl | 1 + src/cascadia/Remoting/Peasant.cpp | 11 +++++-- src/cascadia/Remoting/Peasant.h | 7 +++-- src/cascadia/Remoting/Peasant.idl | 13 ++++++-- src/cascadia/Remoting/WindowActivatedArgs.cpp | 5 +++ src/cascadia/Remoting/WindowActivatedArgs.h | 28 +++++++++++++++++ src/cascadia/Remoting/WindowManager.cpp | 16 ++++++++-- src/cascadia/WindowsTerminal/AppHost.cpp | 8 ++++- 11 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 src/cascadia/Remoting/WindowActivatedArgs.cpp create mode 100644 src/cascadia/Remoting/WindowActivatedArgs.h diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index 3e0b801b29c..0763a84f039 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -22,6 +22,9 @@ Monarch.idl + + Peasant.idl + @@ -42,6 +45,9 @@ Monarch.idl + + Peasant.idl + Create diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cf6392604ff..ec96333bdf2 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -67,6 +67,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Add an event listener to the peasant's WindowActivated event. peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_AddPeasant", + TraceLoggingUInt64(providedID, "providedID", "the provided ID for the peasant"), + TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return newPeasantsId; } @@ -78,17 +84,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - sender: the Peasant that raised this event. This might be out-of-proc! // Return Value: // - - void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& /*args*/) + void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) { // TODO:projects/5 Pass the desktop and timestamp of when the window was // activated in `args`. - if (auto peasant{ sender.try_as() }) - { - auto theirID = peasant.GetID(); - _setMostRecentPeasant(theirID); - } + HandleActivatePeasant(args); + // if (auto peasant{ sender.try_as() }) + // { + // auto theirID = peasant.GetID(); + // } } // Method Description: @@ -125,17 +131,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // That was stupid. I knew it would be but yea it didn't work. // THe Window manager doesn't have a peasant yet when it first creates the monarch. - void Monarch::_setMostRecentPeasant(const uint64_t peasantID) + // + // TODO:MG Make this HandleActivatePeasant(PeasantActivatedArgs args). The + // WindowManager can pass in an args for _peasant.GetLastActivatedArgs() + void Monarch::HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) { // TODO:projects/5 Use a heap/priority queue per-desktop to track which // peasant was the most recent per-desktop. When we want to get the most // recent of all desktops (WindowingBehavior::UseExisting), then use the // most recent of all desktops. - _mostRecentPeasant = peasantID; + _mostRecentPeasant = args.PeasantID(); TraceLoggingWrite(g_hRemotingProvider, - "Monarch_MostRecentPeasantSet", - TraceLoggingUInt64(peasantID, "peasantID", "the ID of the activated peasant"), + "Monarch_SetMostRecentPeasant", + TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 3f58f6916d6..ca114c17502 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); @@ -52,11 +53,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation std::unordered_map _peasants; winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); - void _setMostRecentPeasant(const uint64_t peasantID); uint64_t _getMostRecentPeasantID(); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, - const winrt::Windows::Foundation::IInspectable& args); + const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); friend class RemotingUnitTests::RemotingTests; }; diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 946b3f63c36..299ef9088a3 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -17,6 +17,7 @@ namespace Microsoft.Terminal.Remoting UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); Boolean ProposeCommandline(CommandlineArgs args); + void HandleActivatePeasant(WindowActivatedArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; }; diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index a64373808c1..47af55da293 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -63,12 +63,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _initialArgs; } - void Peasant::ActivateWindow() + void Peasant::ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) { // TODO: projects/5 - somehow, pass an identifier for the current // desktop into this method. The Peasant shouldn't need to be able to // figure it out, but it will need to report it to the monarch. + _lastActivatedArgs = args; + bool successfullyNotified = false; // Raise our WindowActivated event, to let the monarch know we've been // activated. @@ -78,7 +80,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // by the monarch. The monarch might have died. If they have, this // will throw and exception. Just eat it, the election thread will // handle hooking up the new one. - _WindowActivatedHandlers(*this, nullptr); + _WindowActivatedHandlers(*this, args); successfullyNotified = true; } catch (...) @@ -97,4 +99,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // that window, `wt -w 0` does not work right. } + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs() + { + return _lastActivatedArgs; + } + } diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 305c36e310d..4d2bb24a01b 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -21,10 +21,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t GetPID(); bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); - void ActivateWindow(); + void ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); + + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs(); winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); - TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); private: @@ -34,6 +36,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t _id{ 0 }; winrt::Microsoft::Terminal::Remoting::CommandlineArgs _initialArgs{ nullptr }; + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs _lastActivatedArgs{ nullptr }; friend class RemotingUnitTests::RemotingTests; }; diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index b1c5c2acc2d..e3fb5af1b86 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -13,6 +13,14 @@ namespace Microsoft.Terminal.Remoting String CurrentDirectory(); }; + runtimeclass WindowActivatedArgs + { + WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime); + UInt64 PeasantID { get; }; + Guid DesktopID { get; }; + Windows.Foundation.DateTime ActivatedTime { get; }; + }; + interface IPeasant { CommandlineArgs InitialArgs { get; }; @@ -21,9 +29,10 @@ namespace Microsoft.Terminal.Remoting UInt64 GetID(); UInt64 GetPID(); Boolean ExecuteCommandline(CommandlineArgs args); - void ActivateWindow(); + void ActivateWindow(WindowActivatedArgs args); + WindowActivatedArgs GetLastActivatedArgs(); - event Windows.Foundation.TypedEventHandler WindowActivated; + event Windows.Foundation.TypedEventHandler WindowActivated; event Windows.Foundation.TypedEventHandler ExecuteCommandlineRequested; }; diff --git a/src/cascadia/Remoting/WindowActivatedArgs.cpp b/src/cascadia/Remoting/WindowActivatedArgs.cpp new file mode 100644 index 00000000000..4466383c2c7 --- /dev/null +++ b/src/cascadia/Remoting/WindowActivatedArgs.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "WindowActivatedArgs.h" +#include "WindowActivatedArgs.g.cpp" diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h new file mode 100644 index 00000000000..465dd6f8f2b --- /dev/null +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "WindowActivatedArgs.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct WindowActivatedArgs : public WindowActivatedArgsT + { + GETSET_PROPERTY(uint64_t, PeasantID, 0); + GETSET_PROPERTY(winrt::guid, DesktopID, {}); + GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {}); + + public: + WindowActivatedArgs(uint64_t peasantID, winrt::guid desktopID, winrt::Windows::Foundation::DateTime timestamp) : + _PeasantID{ peasantID }, + _DesktopID{ desktopID }, + _ActivatedTime{ timestamp } {}; + }; +} + +namespace winrt::Microsoft::Terminal::Remoting::factory_implementation +{ + BASIC_FACTORY(WindowActivatedArgs); +} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index d7428782d07..e76af3d9a00 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -117,6 +117,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingBoolean(isKing, "isKing", "true if we are the new monarch"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + if (_peasant) + { + // Oh, don't do this, we do this in the election night + // // Tell the monarch about our peasant + // _monarch.AddPeasant(_peasant); + + // Inform the monarch of the time we were last activated + _monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs()); + } + if (!isKing) { return; @@ -125,7 +135,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // // This is where you should do any aditional setup that might need to be // done when we become the king. THis will be called both for the first - // window, and when the current monarch diesd. + // window, and when the current monarch dies. // Wait, don't. Let's just have the monarch try/catch any accesses to // peasants. If the peasant dies, then it can't get the peasant's @@ -135,9 +145,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // winrt::com_ptr monarchImpl; // monarchImpl.copy_from(winrt::get_self(_monarch)); - // if (monarchImpl) + // if (monarchImpl && _peasant) // { - // monarchImpl->SetMostRecentPeasant(_peasant.GetID()); + // monarchImpl->HandleActivatePeasant(_peasant.GetLastActivatedArgs()); // } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 11e71414eb9..af0c6a7e334 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -534,6 +534,12 @@ void AppHost::_WindowActivated() { if (auto peasant{ _windowManager.CurrentWindow() }) { - peasant.ActivateWindow(); + // TODO: projects.5 - in the future, we'll want to actually get the + // desktip GUID in IslandWindow, and bubble that up here, then down to + // the Peasant. For now, we're just leaving space for it. + winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs args{ peasant.GetID(), + winrt::guid{}, + winrt::clock().now() }; + peasant.ActivateWindow(args); } } From a75da0a1045291182bffd37d66561f9effa11263 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 06:33:44 -0600 Subject: [PATCH 30/76] mostly just notes --- src/cascadia/Remoting/Monarch.cpp | 42 ++++++++++++------------- src/cascadia/Remoting/Monarch.h | 2 ++ src/cascadia/Remoting/WindowManager.cpp | 15 --------- src/cascadia/Remoting/WindowManager.h | 2 -- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ec96333bdf2..ddf00470dc9 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -82,19 +82,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // window". // Arguments: // - sender: the Peasant that raised this event. This might be out-of-proc! + // - args: a bundle of the peasant ID, timestamp, and desktop ID, for the activated peasant // Return Value: // - void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) + const Remoting::WindowActivatedArgs& args) { - // TODO:projects/5 Pass the desktop and timestamp of when the window was - // activated in `args`. - HandleActivatePeasant(args); - // if (auto peasant{ sender.try_as() }) - // { - // auto theirID = peasant.GetID(); - // } } // Method Description: @@ -124,27 +118,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } - // TODO:MG This probably shouldn't be a public function. I'm making it - // public so the WindowManager can use it to manually tell the monarch it's - // own ID to use as the MRU, when the monarch is first instantiated. THat's - // dumb, but it's a hack to get something working. - // - // That was stupid. I knew it would be but yea it didn't work. - // THe Window manager doesn't have a peasant yet when it first creates the monarch. - // - // TODO:MG Make this HandleActivatePeasant(PeasantActivatedArgs args). The - // WindowManager can pass in an args for _peasant.GetLastActivatedArgs() - void Monarch::HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) + void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args) { // TODO:projects/5 Use a heap/priority queue per-desktop to track which // peasant was the most recent per-desktop. When we want to get the most // recent of all desktops (WindowingBehavior::UseExisting), then use the // most recent of all desktops. - _mostRecentPeasant = args.PeasantID(); + const auto oldLastActiveTime = _lastActivatedTime.time_since_epoch().count(); + const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count(); + + // For now, we'll just pay attention to whoever the most recent peasant + // was. We're not too wooried about the mru peasant dying. Worst case - + // when the user executes a `wt -w 0`, we won't be able to find that + // peasant, and it'll open in a new window instead of the current one. + if (args.ActivatedTime() > _lastActivatedTime) + { + _mostRecentPeasant = args.PeasantID(); + _lastActivatedTime = args.ActivatedTime(); + } TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"), + TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"), + TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided args.ActivatedTime()"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } @@ -153,7 +150,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_mostRecentPeasant == 0) { // We haven't yet been told the MRU peasant. Just use the first one. - // TODO:MG GOD this is just gonna be a random one. Hacks on hacks on hacks + // This is just gonna be a random one, but really shouldn't happen + // in practice. The WindowManager should set the MRU peasant + // immediately as soon as it creates the monarch/peasant for the + // first window. if (_peasants.size() > 0) { return _peasants.begin()->second.GetID(); diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index ca114c17502..70c210e4784 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -49,6 +49,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t _nextPeasantID{ 1 }; uint64_t _thisPeasantID{ 0 }; uint64_t _mostRecentPeasant{ 0 }; + winrt::Windows::Foundation::DateTime _lastActivatedTime{}; + WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew }; std::unordered_map _peasants; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index e76af3d9a00..1e0c0045757 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -119,10 +119,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_peasant) { - // Oh, don't do this, we do this in the election night - // // Tell the monarch about our peasant - // _monarch.AddPeasant(_peasant); - // Inform the monarch of the time we were last activated _monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs()); } @@ -137,18 +133,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // done when we become the king. THis will be called both for the first // window, and when the current monarch dies. - // Wait, don't. Let's just have the monarch try/catch any accesses to - // peasants. If the peasant dies, then it can't get the peasant's - // anything. In that case, _remove it_. - _monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested }); - - // winrt::com_ptr monarchImpl; - // monarchImpl.copy_from(winrt::get_self(_monarch)); - // if (monarchImpl && _peasant) - // { - // monarchImpl->HandleActivatePeasant(_peasant.GetLastActivatedArgs()); - // } } bool WindowManager::_areWeTheKing() diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index b87ce94d646..020446d7da7 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -20,8 +20,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow(); - // Don't do this, the monarch can and will change over time - // FORWARDED_TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs, _monarch, FindTargetWindowRequested); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); private: From c02f25aba3f0549173c257ff3c5b8a62f6afa507 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 08:00:51 -0600 Subject: [PATCH 31/76] peasants now switch to the cwd that was requested --- OpenConsole.sln | 11 ++++-- src/cascadia/Remoting/Peasant.cpp | 6 +++ src/cascadia/TerminalApp/AppLogic.cpp | 6 ++- src/cascadia/TerminalApp/AppLogic.h | 2 +- src/cascadia/TerminalApp/AppLogic.idl | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 48 ++++++++++++++++++++++- src/cascadia/TerminalApp/TerminalPage.h | 4 +- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 8 files changed, 69 insertions(+), 12 deletions(-) diff --git a/OpenConsole.sln b/OpenConsole.sln index eaf51110a64..a70034cfaab 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -355,6 +355,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Remoting", "src\c {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wpf", "wpf", "{4DAF0299-495E-4CD1-A982-9BAC16A45932}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -2157,7 +2159,7 @@ Global {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.ActiveCfg = Release|x64 - {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.Build.0 = AuditMode|x64 + {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x64.Build.0 = Release|x64 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.ActiveCfg = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.AuditMode|x86.Build.0 = AuditMode|Win32 {43CE4CE5-0010-4B99-9569-672670D26E26}.Debug|Any CPU.ActiveCfg = Debug|Win32 @@ -2287,8 +2289,8 @@ Global {05500DEF-2294-41E3-AF9A-24E580B82836} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} {1E4A062E-293B-4817-B20D-BF16B979E350} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} - {84848BFA-931D-42CE-9ADF-01EE54DE7890} = {59840756-302F-44DF-AA47-441A9D673202} - {376FE273-6B84-4EB5-8B30-8DE9D21B022C} = {59840756-302F-44DF-AA47-441A9D673202} + {84848BFA-931D-42CE-9ADF-01EE54DE7890} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} + {376FE273-6B84-4EB5-8B30-8DE9D21B022C} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} {CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} {CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} @@ -2308,7 +2310,7 @@ Global {024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} {067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202} {6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87} - {1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202} + {1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {4DAF0299-495E-4CD1-A982-9BAC16A45932} {506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202} @@ -2317,6 +2319,7 @@ Global {43CE4CE5-0010-4B99-9569-672670D26E26} = {59840756-302F-44DF-AA47-441A9D673202} {27B5AAEB-A548-44CF-9777-F8BAA32AF7AE} = {59840756-302F-44DF-AA47-441A9D673202} {68A10CD3-AA64-465B-AF5F-ED4E9700543C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {4DAF0299-495E-4CD1-A982-9BAC16A45932} = {59840756-302F-44DF-AA47-441A9D673202} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 47af55da293..659a2b293f8 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -50,6 +50,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _initialArgs = args; } + TraceLoggingWrite(g_hRemotingProvider, + "Peasant_ExecuteCommandline", + TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), + TraceLoggingWideString(args.CurrentDirectory().c_str(), "directory", "the provided cwd"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // Raise an event with these args. The AppHost will listen for this // event to know when to take these args and dispatch them to a // currently-running window. diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 7393b248a80..2a1c2dde273 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1112,14 +1112,16 @@ namespace winrt::TerminalApp::implementation return result; } - int32_t AppLogic::ExecuteCommandline(array_view args) + int32_t AppLogic::ExecuteCommandline(array_view args, + const winrt::hstring& cwd) { ::TerminalApp::AppCommandlineArgs appArgs; auto result = appArgs.ParseArgs(args); if (result == 0) { auto actions = winrt::single_threaded_vector(std::move(appArgs.GetStartupActions())); - _root->ProcessStartupActions(actions, false); + + _root->ProcessStartupActions(actions, false, cwd); } return result; // TODO:MG does a return value make sense diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index d0b39a73f57..13c7885d611 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -29,7 +29,7 @@ namespace winrt::TerminalApp::implementation [[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept; int32_t SetStartupCommandline(array_view actions); - int32_t ExecuteCommandline(array_view actions); + int32_t ExecuteCommandline(array_view actions, const winrt::hstring& cwd); int32_t FindTargetWindow(array_view actions); winrt::hstring ParseCommandlineMessage(); bool ShouldExitEarly(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index db50878a225..b38e0fe0303 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -29,7 +29,7 @@ namespace TerminalApp Boolean IsElevated(); Int32 SetStartupCommandline(String[] commands); - Int32 ExecuteCommandline(String[] commands); + Int32 ExecuteCommandline(String[] commands, String cwd); String ParseCommandlineMessage { get; }; Boolean ShouldExitEarly { get; }; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index cf3c3bb7c74..830e85ea7dc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -349,7 +349,8 @@ namespace winrt::TerminalApp::implementation // Return Value: // - winrt::fire_and_forget TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector actions, - const bool initial) + const bool initial, + const winrt::hstring cwd) { // If there are no actions left, do nothing. if (actions.Size() == 0) @@ -360,6 +361,30 @@ namespace winrt::TerminalApp::implementation // Handle it on a subsequent pass of the UI thread. co_await winrt::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal); + + // If the caller provided a CWD, switch to that directory, then switch + // back once we're done. This looks weird though, because we have to set + // up the scope_exit _first_. We'll release the scope_exit if we don't + // actually need it. + std::wstring originalCwd{ wil::GetCurrentDirectoryW() }; + auto restoreCwd = wil::scope_exit([&originalCwd]() { + // ignore errors, we'll just power on through. We'd rather do + // something rather than fail silently if the direcotry doesn't + // actually exist. + LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(originalCwd.c_str())); + }); + if (cwd.empty()) + { + restoreCwd.release(); + } + else + { + // ignore errors, we'll just power on through. We'd rather do + // something rather than fail silently if the direcotry doesn't + // actually exist. + LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(cwd.c_str())); + } + if (auto page{ weakThis.get() }) { for (const auto& action : actions) @@ -861,9 +886,28 @@ namespace winrt::TerminalApp::implementation envMap.Insert(L"WT_PROFILE_ID", guidWString); envMap.Insert(L"WSLENV", L"WT_PROFILE_ID"); + // Update the path to be relative to whatever our CWD is. + // + // Refer to the examples in + // https://en.cppreference.com/w/cpp/filesystem/path/append + // + // We need to do this here, to ensure we tell the ConptyConnection + // the correct starting path. If we're being invoked from another + // terminal instance (e.g. wt -w 0 -d .), then we have switched our + // CWD to the provided path. We should treat the StartingDirectory + // as relative to the current CWD. + // + // The connection must be informed of the current CWD on + // construction, because the connection might not spawn the child + // process until later, on another thread, after we've already + // restored the CWD to it's original value. + std::wstring cwdString{ wil::GetCurrentDirectoryW() }; + std::filesystem::path cwd{ cwdString }; + cwd /= settings.StartingDirectory().c_str(); + auto conhostConn = TerminalConnection::ConptyConnection( settings.Commandline(), - settings.StartingDirectory(), + winrt::hstring{ cwd.c_str() }, settings.StartingTitle(), envMap.GetView(), settings.InitialRows(), diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 58709e86e8d..9b72dbdb590 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -81,7 +81,9 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning(); winrt::hstring KeyboardServiceDisabledText(); - winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, const bool initial); + winrt::fire_and_forget ProcessStartupActions(Windows::Foundation::Collections::IVector actions, + const bool initial, + const winrt::hstring cwd = L""); // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index af0c6a7e334..29fb6e691ca 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -520,7 +520,7 @@ bool AppHost::HasWindow() void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) { - _logic.ExecuteCommandline(args.Args()); + _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); } void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, From 3e39ab9e717d62dcfbda110eed5e41077f3c3152 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 08:29:35 -0600 Subject: [PATCH 32/76] more comments --- src/cascadia/TerminalApp/AppLogic.cpp | 27 ++++++++++++++++++++++-- src/cascadia/WindowsTerminal/AppHost.cpp | 24 +++++++++++++++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 2a1c2dde273..77f9b94c105 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1112,6 +1112,22 @@ namespace winrt::TerminalApp::implementation return result; } + // Method Description: + // - Parse the provided commandline arguments into actions, and try to + // perform them immediately. + // - This function returns 0, unless a there was a non-zero result from + // trying to parse one of the commands provided. In that case, no commands + // after the failing command will be parsed, and the non-zero code + // returned. + // - If a non-empty cwd is provided, the entire terminal exe will switch to + // that CWD while we handle these actions, then return to the original + // CWD. + // Arguments: + // - args: an array of strings to process as a commandline. These args can contain spaces + // - cwd: The directory to use as the CWD while performing thse actions. + // Return Value: + // - the result of the first command who's parsing returned a non-zero code, + // or 0. (see AppLogic::_ParseArgs) int32_t AppLogic::ExecuteCommandline(array_view args, const winrt::hstring& cwd) { @@ -1123,8 +1139,8 @@ namespace winrt::TerminalApp::implementation _root->ProcessStartupActions(actions, false, cwd); } - - return result; // TODO:MG does a return value make sense + // Return the result of parsing with commandline, though it may or may not be used. + return result; } // Method Description: @@ -1166,6 +1182,13 @@ namespace winrt::TerminalApp::implementation // to handle the commandline iteslf, and find that the commandline // failed to parse. When that happens, the new window will display the // message box. + // + // This will also work for the case where the user specifies an invalid + // commandline in conjunction with `-w 0`. This function will determine + // that the commandline hasa parse error, and indicate that we should + // create a new window. Then, in that new window, we'll try to set the + // StartupActions, which will again fail, returning the correct error + // message. return -1; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 29fb6e691ca..2b124497b8f 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -517,12 +517,32 @@ bool AppHost::HasWindow() return _shouldCreateWindow; } +// Method Description: +// - Event handler for the Peasant::ExecuteCommandlineRequested event. Take the +// provided commandline args, and attempt to parse them and perform the +// actions immediately. The parsing is performed by AppLogic. +// - This is invoked when another wt.exe instance runs something like `wt -w 1 +// new-tab`, and the Monarch delegates the commandline to this instance. +// Arguments: +// - args: the bundle of a commandline and working directory to use for this invocation. +// Return Value: +// - void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) { _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); } +// Method Description: +// - Event handler for the WindowManager::FindTargetWindowRequested event. The +// manager will ask us how to figure out what the target window is for a set +// of commandline arguments. We'll take those arguments, and ask AppLogic to +// parse them for us. We'll then set ResultTargetWindow in the given args, so +// the sender can use that reuslt. +// Arguments: +// - args: the bundle of a commandline and working directory to find the correct target window for. +// Return Value: +// - void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) { @@ -534,8 +554,8 @@ void AppHost::_WindowActivated() { if (auto peasant{ _windowManager.CurrentWindow() }) { - // TODO: projects.5 - in the future, we'll want to actually get the - // desktip GUID in IslandWindow, and bubble that up here, then down to + // TODO: projects/5 - in the future, we'll want to actually get the + // desktop GUID in IslandWindow, and bubble that up here, then down to // the Peasant. For now, we're just leaving space for it. winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs args{ peasant.GetID(), winrt::guid{}, From 52b2cb6d3f65d371e71b34aa170078585c240464 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 10:39:07 -0600 Subject: [PATCH 33/76] Allow the user to provide ids on the commandline --- .../Microsoft.Terminal.RemotingLib.vcxproj | 6 +++ src/cascadia/Remoting/Monarch.cpp | 18 ++++++-- src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Monarch.idl | 8 +++- .../Remoting/ProposeCommandlineResult.cpp | 5 +++ .../Remoting/ProposeCommandlineResult.h | 17 +++++++ src/cascadia/Remoting/WindowManager.cpp | 45 +++++++++++++++++-- src/cascadia/Remoting/WindowManager.h | 2 +- 8 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 src/cascadia/Remoting/ProposeCommandlineResult.cpp create mode 100644 src/cascadia/Remoting/ProposeCommandlineResult.h diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index 0763a84f039..d0e9882845a 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -22,6 +22,9 @@ Monarch.idl + + Monarch.idl + Peasant.idl @@ -45,6 +48,9 @@ Monarch.idl + + Monarch.idl + Peasant.idl diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ddf00470dc9..f03137bc1d8 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -5,6 +5,7 @@ #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" +#include "ProposeCommandlineResult.h" #include "Monarch.g.cpp" #include "../../types/inc/utils.hpp" @@ -176,7 +177,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - true if the caller should create a new window for this commandline. // False otherwise - the monarch should have dispatched this commandline // to another window in this case. - bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& args) + Remoting::ProposeCommandlineResult Monarch::ProposeCommandline(const Remoting::CommandlineArgs& args) { // Raise an event, to ask how to handle this commandline. We can't ask // the app ourselves - we exist isolated from that knowledge (and @@ -215,9 +216,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - return false; + auto result = winrt::make_self(); + result->ShouldCreateWindow(false); + return *result; } - else + else if (windowID > 0) { // TODO:MG in this case, an ID was provided, but there's no // peasant with that ID. Instead, we should tell the caller that @@ -231,6 +234,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + auto result = winrt::make_self(); + result->ShouldCreateWindow(true); + result->Id(windowID); + return *result; } } @@ -239,7 +246,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // TODO:MG in this case, no usable ID was provided. Return { true, nullopt } - return true; + // return true; + auto result = winrt::make_self(); + result->ShouldCreateWindow(true); + return *result; } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 70c210e4784..266b8cc89f1 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -37,7 +37,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); - bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); + winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 299ef9088a3..206a48d05db 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -11,12 +11,18 @@ namespace Microsoft.Terminal.Remoting Int32 ResultTargetWindow; } + [default_interface] runtimeclass ProposeCommandlineResult { + Windows.Foundation.IReference Id { get; }; + // TOOD:projects/5 - also return the name here, if the name was set on the commandline + Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode + } + [default_interface] runtimeclass Monarch { Monarch(); UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); - Boolean ProposeCommandline(CommandlineArgs args); + ProposeCommandlineResult ProposeCommandline(CommandlineArgs args); void HandleActivatePeasant(WindowActivatedArgs args); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.cpp b/src/cascadia/Remoting/ProposeCommandlineResult.cpp new file mode 100644 index 00000000000..fc9cc8f616b --- /dev/null +++ b/src/cascadia/Remoting/ProposeCommandlineResult.cpp @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" +#include "ProposeCommandlineResult.h" +#include "ProposeCommandlineResult.g.cpp" diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.h b/src/cascadia/Remoting/ProposeCommandlineResult.h new file mode 100644 index 00000000000..c45f134a1ac --- /dev/null +++ b/src/cascadia/Remoting/ProposeCommandlineResult.h @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "ProposeCommandlineResult.g.h" +#include "../cascadia/inc/cppwinrt_utils.h" + +namespace winrt::Microsoft::Terminal::Remoting::implementation +{ + struct ProposeCommandlineResult : public ProposeCommandlineResultT + { + public: + GETSET_PROPERTY(Windows::Foundation::IReference, Id); + GETSET_PROPERTY(bool, ShouldCreateWindow, true); + }; +} diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 1e0c0045757..64495bbd8ae 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -49,8 +49,41 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // launched with them! // // Otherwise, the King will tell us if we should make a new window - _shouldCreateWindow = isKing || - _monarch.ProposeCommandline(args); + _shouldCreateWindow = isKing; + std::optional givenID; + + if (!isKing) + { + auto result = _monarch.ProposeCommandline(args); + _shouldCreateWindow = result.ShouldCreateWindow(); + if (result.Id()) + { + givenID = result.Id().Value(); + } + if (givenID) + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingPointer(nullptr, "Id", "No ID provided"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline_AsMonarch", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } // TODO:projects/5 The monarch may respond back "you should be a new // window, with ID,name of (id, name)". Really the responses are: @@ -65,7 +98,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // If we should create a new window, then instantiate our Peasant // instance, and tell that peasant to handle that commandline. - _createOurPeasant(); + _createOurPeasant({ givenID }); // Spawn a thread to wait on the monarch, and handle the election if (!isKing) @@ -143,9 +176,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return (ourPID == kingPID); } - Remoting::IPeasant WindowManager::_createOurPeasant() + Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID) { auto p = winrt::make_self(); + if (givenID) + { + p->AssignID(givenID.value()); + } _peasant = *p; _monarch.AddPeasant(_peasant); diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 020446d7da7..5e52210cb33 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -36,7 +36,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _createMonarch(); void _createMonarchAndCallbacks(); bool _areWeTheKing(); - winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(); + winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional givenID); bool _electionNight2020(); void _createPeasantThread(); From bc492f1815f7639735d45f753904ee82599e11c9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 10:45:46 -0600 Subject: [PATCH 34/76] Code cleanup --- src/cascadia/Remoting/Monarch.cpp | 19 +++++++++---------- src/cascadia/Remoting/WindowManager.cpp | 23 ++++++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f03137bc1d8..fa15636a72a 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -205,10 +205,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (auto targetPeasant{ _getPeasant(windowID) }) { + // This will raise the peasant's ExecuteCommandlineRequested + // event, which will then ask the AppHost to handle the + // commandline, which will then pass it to AppLogic for + // handling. targetPeasant.ExecuteCommandline(args); - // TODO:MG if the targeted peasant fails to execute the - // commandline, we should create our own window to display the - // message box. TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", @@ -222,18 +223,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } else if (windowID > 0) { - // TODO:MG in this case, an ID was provided, but there's no + // In this case, an ID was provided, but there's no // peasant with that ID. Instead, we should tell the caller that // they should make a new window, but _with that ID_. - // - // `Monarch::ProposeCommandline` needs to return a structure of - // `{ shouldCreateWindow: bool, givenID: optional }` - // + TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + auto result = winrt::make_self(); result->ShouldCreateWindow(true); result->Id(windowID); @@ -245,8 +244,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "Monarch_ProposeCommandline_NewWindow", TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // TODO:MG in this case, no usable ID was provided. Return { true, nullopt } - // return true; + + // In this case, no usable ID was provided. Return { true, nullopt } auto result = winrt::make_self(); result->ShouldCreateWindow(true); return *result; diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 64495bbd8ae..08234b2ba95 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -54,12 +54,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (!isKing) { + // The monarch may respond back "you should be a new + // window, with ID,name of (id, name)". Really the responses are: + // * You should not create a new window + // * Create a new window (but without a given ID or name). The + // Monarch will assign your ID/name later + // * Create a new window, and you'll have this ID or name + // - This is the case where the user provides `wt -w 1`, and + // there's no existing window 1 + auto result = _monarch.ProposeCommandline(args); _shouldCreateWindow = result.ShouldCreateWindow(); if (result.Id()) { givenID = result.Id().Value(); } + + // TraceLogging doesn't have a good solution for logging an + // optional. So we have to repeat the calls here: if (givenID) { TraceLoggingWrite(g_hRemotingProvider, @@ -79,21 +91,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } else { + // We're the monarch, we don't need to propose anything. We're just + // going to do it. TraceLoggingWrite(g_hRemotingProvider, "WindowManager_ProposeCommandline_AsMonarch", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - // TODO:projects/5 The monarch may respond back "you should be a new - // window, with ID,name of (id, name)". Really the responses are: - // * You should not create a new window - // * Create a new window (but without a given ID or name). The Monarch - // will assign your ID/name later - // * Create a new window, and you'll have this ID or name - // - This is the case where the user provides `wt -w 1`, and there's - // no existing window 1 - if (_shouldCreateWindow) { // If we should create a new window, then instantiate our Peasant From be74b2ee2dd1fb23dc8921d7b6b8480d25b6a478 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 15:18:26 -0600 Subject: [PATCH 35/76] putting var in headers is bad, mkay? --- src/cascadia/Remoting/MonarchFactory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h index 5e9add11a3c..07c2e29a857 100644 --- a/src/cascadia/Remoting/MonarchFactory.h +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -12,14 +12,14 @@ // Future callers who try to instantiate a Monarch will get the one that's // already been made. -winrt::weak_ref g_weak{ nullptr }; - struct MonarchFactory : winrt::implements { MonarchFactory() = default; HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept { + static winrt::weak_ref g_weak{ nullptr }; + *result = nullptr; if (outer) { From 2a7bc94a8ffa6e710cc07794446e49d2a7c19e7c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 7 Jan 2021 16:40:49 -0600 Subject: [PATCH 36/76] branding --- src/cascadia/Remoting/Monarch.h | 25 ++++++++++++++++++++----- src/cascadia/Remoting/MonarchFactory.h | 8 ++++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 374b30ec391..eeeb9fe7602 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -7,12 +7,27 @@ #include "Peasant.h" #include "../cascadia/inc/cppwinrt_utils.h" -// {06171993-7eb1-4f3e-85f5-8bdd7386cce3} -constexpr GUID Monarch_clsid{ +// We sure different GUIDs here depending on whether we're running a Release, +// Preview, or Dev build. This ensures that different installs don't +// accidentally talk to one another. +// +// * Release: {06171993-7eb1-4f3e-85f5-8bdd7386cce3} +// * Preview: {04221993-7eb1-4f3e-85f5-8bdd7386cce3} +// * Dev: {08302020-7eb1-4f3e-85f5-8bdd7386cce3} +constexpr GUID Monarch_clsid +{ +#if defined(WT_BRANDING_RELEASE) 0x06171993, - 0x7eb1, - 0x4f3e, - { 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 } +#elif defined(WT_BRANDING_PREVIEW) + 0x04221993, +#else + 0x08302020, +#endif + 0x7eb1, + 0x4f3e, + { + 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 + } }; enum class WindowingBehavior : uint64_t diff --git a/src/cascadia/Remoting/MonarchFactory.h b/src/cascadia/Remoting/MonarchFactory.h index 07c2e29a857..496a6094686 100644 --- a/src/cascadia/Remoting/MonarchFactory.h +++ b/src/cascadia/Remoting/MonarchFactory.h @@ -26,18 +26,18 @@ struct MonarchFactory : winrt::implements return CLASS_E_NOAGGREGATION; } - if (!g_weak) + // Lock the ref immediately. We don't want it freed from out beneath us + auto strong = g_weak.get(); + if (!strong) { // Create a new Monarch instance - auto strong = winrt::make_self(); - + strong = winrt::make_self(); g_weak = (*strong).get_weak(); return strong.as(iid, result); } else { // We already instantiated one Monarch, let's just return that one! - auto strong = g_weak.get(); return strong.as(iid, result); } } From 5b8ace276b0eb0752090c8ce6e98fa93e7510ca9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 8 Jan 2021 12:58:43 -0600 Subject: [PATCH 37/76] A bunch of tests for Monarch::ProposeCommandline --- .../UnitTests_Remoting/RemotingTests.cpp | 263 +++++++++++++++++- src/cascadia/WindowsTerminal/AppHost.cpp | 10 +- 2 files changed, 262 insertions(+), 11 deletions(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index dd3b5ad123d..84cf1e932db 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -3,6 +3,9 @@ #include "pch.h" #include "../Remoting/Monarch.h" +#include "../Remoting/CommandlineArgs.h" +#include "../Remoting/FindTargetWindowArgs.h" +#include "../Remoting/ProposeCommandlineResult.h" using namespace Microsoft::Console; using namespace WEX::Logging; @@ -34,18 +37,26 @@ namespace RemotingUnitTests { // This is a silly helper struct. // It will always throw an hresult_error on any of it's methods. - // In the tests, it's hard to emulate a peasant process being totally dead once the Monarch has captured a reference to it. Since everything's in-proc in the tests, we can't decrement the refcount in such a way that the monarch's reference will throw a catchable exception. - // Instead, this class can be used to replace a peasant inside a Monarch, to emulate that peasant process dying. Any time the monarch tries to do something to this peasant, it'll throw an exception. + // + // In the tests, it's hard to emulate a peasant process being totally dead + // once the Monarch has captured a reference to it. Since everything's + // in-proc in the tests, we can't decrement the refcount in such a way that + // the monarch's reference will throw a catchable exception. Instead, this + // class can be used to replace a peasant inside a Monarch, to emulate that + // peasant process dying. Any time the monarch tries to do something to this + // peasant, it'll throw an exception. struct DeadPeasant : implements { DeadPeasant() = default; void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; }; uint64_t GetID() { throw winrt::hresult_error{}; }; uint64_t GetPID() { throw winrt::hresult_error{}; }; - bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } - winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } - TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); - TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); + bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } + void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { throw winrt::hresult_error{}; } + Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; } + Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error{}; } + TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs); + TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs); }; class RemotingTests @@ -61,6 +72,13 @@ namespace RemotingUnitTests TEST_METHOD(AddPeasantsToNewMonarch); TEST_METHOD(RemovePeasantFromMonarchWhenFreed); + TEST_METHOD(ProposeCommandlineNoWindow); + TEST_METHOD(ProposeCommandlineGivenWindow); + TEST_METHOD(ProposeCommandlineNegativeWindow); + TEST_METHOD(ProposeCommandlineCurrentWindow); + TEST_METHOD(ProposeCommandlineNonExistentWindow); + TEST_METHOD(ProposeCommandlineDeadWindow); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -68,8 +86,14 @@ namespace RemotingUnitTests static void _killPeasant(const com_ptr& m, const uint64_t peasantID); + + static void _findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; + // Helper to replace the specified peasant in a monarch with a + // "DeadPeasant", which will emulate what happens when the peasant process + // dies. void RemotingTests::_killPeasant(const com_ptr& m, const uint64_t peasantID) { @@ -83,6 +107,17 @@ namespace RemotingUnitTests m->_peasants[peasantID] = *tombstone; } + void RemotingTests::_findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + { + auto arguments = args.Args().Args(); + if (arguments.size() > 0) + { + auto index = std::stoi(arguments.at(0).c_str()); + args.ResultTargetWindow(index); + } + } + void RemotingTests::CreateMonarch() { auto m1 = winrt::make_self(); @@ -292,4 +327,220 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); } + void RemotingTests::ProposeCommandlineNoWindow() + { + Log::Comment(L"Test proposing a commandline that doesn't have a window specified in it"); + + const auto monarch0PID = 12345u; + + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + std::vector args{}; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + Log::Comment(L"Propose the same args again after adding a peasant - we should still return {create new window, no ID}"); + result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + + void RemotingTests::ProposeCommandlineGivenWindow() + { + Log::Comment(L"Test proposing a commandline for a window that currently exists"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p1"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Args().at(1)); + }); + + std::vector args{ L"1", L"arg[1]" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + void RemotingTests::ProposeCommandlineNegativeWindow() + { + Log::Comment(L"Test proposing a commandline for an invalid window ID, like -1"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + { + std::vector args{ L"-1" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + { + std::vector args{ L"-2" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + } + void RemotingTests::ProposeCommandlineCurrentWindow() + { + Log::Comment(L"Test proposing a commandline for the current window (ID=0)"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p1"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Args().at(1)); + }); + + std::vector p1Args{ L"0", L"arg[1]" }; + std::vector p2Args{ L"0", L"this is for p2" }; + + { + Log::Comment(L"Manually activate the first peasant"); + // This would usually happen immediately when the window is created, but + // there's no actual window in these tests. + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + winrt::guid{}, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + + Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p2"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); + }); + + { + Log::Comment(L"Activate the second peasant"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + winrt::guid{}, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + + Log::Comment(L"Send a commandline to the current window, which should be p2"); + Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } }; + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + { + Log::Comment(L"Reactivate the first peasant"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + winrt::guid{}, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + + Log::Comment(L"Send a commandline to the current window, which should be p1 again"); + Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } }; + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + } + void RemotingTests::ProposeCommandlineNonExistentWindow() + { + Log::Comment(L"Test proposing a commandline for an ID that doesn't have a current peasant"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + { + std::vector args{ L"2" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(true, (bool)result.Id()); + VERIFY_ARE_EQUAL(2u, result.Id().Value()); + } + { + std::vector args{ L"10" }; + Remoting::CommandlineArgs eventArgs{ { args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(true, (bool)result.Id()); + VERIFY_ARE_EQUAL(10u, result.Id().Value()); + } + } + void RemotingTests::ProposeCommandlineDeadWindow() + { + Log::Comment(L"Test proposing a commandline for a peasant that previously died"); + VERIFY_ARE_EQUAL(true, false, L"TODO: Finish this test"); + } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 2b124497b8f..6fa2794b192 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -528,7 +528,7 @@ bool AppHost::HasWindow() // Return Value: // - void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, - winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) + Remoting::CommandlineArgs args) { _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); } @@ -544,7 +544,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se // Return Value: // - void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) + const Remoting::FindTargetWindowArgs& args) { const auto targetWindow = _logic.FindTargetWindow(args.Args().Args()); args.ResultTargetWindow(targetWindow); @@ -557,9 +557,9 @@ void AppHost::_WindowActivated() // TODO: projects/5 - in the future, we'll want to actually get the // desktop GUID in IslandWindow, and bubble that up here, then down to // the Peasant. For now, we're just leaving space for it. - winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs args{ peasant.GetID(), - winrt::guid{}, - winrt::clock().now() }; + Remoting::WindowActivatedArgs args{ peasant.GetID(), + winrt::guid{}, + winrt::clock().now() }; peasant.ActivateWindow(args); } } From 689c38519b7cdce44731f04682809a855631d462 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 8 Jan 2021 13:00:33 -0600 Subject: [PATCH 38/76] sure yea that's a doc comment --- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 84cf1e932db..dd635eecd1f 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -107,6 +107,8 @@ namespace RemotingUnitTests m->_peasants[peasantID] = *tombstone; } + // Helper to get the first argument out of the commandline, and try to + // convert it to an int. void RemotingTests::_findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) { From f02969b70f21d2603f5753458c514babf0d45c25 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Jan 2021 11:07:15 -0600 Subject: [PATCH 39/76] More tests, more redundancy --- src/cascadia/Remoting/Peasant.cpp | 19 +- src/cascadia/Remoting/WindowManager.cpp | 182 ++++++++++++------ src/cascadia/Remoting/WindowManager.h | 2 +- .../UnitTests_Remoting/RemotingTests.cpp | 56 +++++- 4 files changed, 195 insertions(+), 64 deletions(-) diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 659a2b293f8..699adf976b2 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -69,12 +69,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _initialArgs; } - void Peasant::ActivateWindow(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args) + void Peasant::ActivateWindow(const Remoting::WindowActivatedArgs& args) { // TODO: projects/5 - somehow, pass an identifier for the current // desktop into this method. The Peasant shouldn't need to be able to // figure it out, but it will need to report it to the monarch. + // Store these new args as our last activated state. If a new monarch + // comes looking, we can use this info to tell them when we were last + // activated. _lastActivatedArgs = args; bool successfullyNotified = false; @@ -91,7 +94,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } catch (...) { - // TODO:MG Tracelogging LOG_CAUGHT_EXCEPTION(); } @@ -100,12 +102,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingUInt64(GetID(), "peasantID", "Our ID"), TraceLoggingBoolean(successfullyNotified, "successfullyNotified", "true if we successfully notified the monarch"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // TODO:MG Open three windows, close the first (the monarch). The focus - // should automatically move to the third, from the windows shell. In - // that window, `wt -w 0` does not work right. } - winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs() + // Method Description: + // - Retrieve the WindowActivatedArgs describing the last activation of this + // peasant. New monarchs can use this state to determine when we were last + // activated. + // Arguments: + // - + // Return Value: + // - a WindowActivatedArgs with info about when and where we were last activated. + Remoting::WindowActivatedArgs Peasant::GetLastActivatedArgs() { return _lastActivatedArgs; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 08234b2ba95..df7006b3ca4 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -199,7 +199,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return _peasant; } - bool WindowManager::_electionNight2020() + // Method Description: + // - Attempt to connect to the monarch process. This might be us! + // - For the new monarch, add us to their list of peasants. + // Arguments: + // - + // Return Value: + // - true iff we're the new monarch process. + bool WindowManager::_performElection() { _createMonarchAndCallbacks(); @@ -208,10 +215,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (_areWeTheKing()) { - // This is only called when a _new_ monarch is elected. So don't do - // anything here that needs to be done for all monarch windows. This - // should only be for work that's done when a window _becomes_ a - // monarch, after the death of the previous monarch. + // This method is only called when a _new_ monarch is elected. So + // don't do anything here that needs to be done for all monarch + // windows. This should only be for work that's done when a window + // _becomes_ a monarch, after the death of the previous monarch. return true; } return false; @@ -233,63 +240,126 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { HANDLE waits[2]; waits[1] = _monarchWaitInterrupt.get(); + const auto peasantID = _peasant.GetID(); - bool exitRequested = false; - while (!exitRequested) + bool exitThreadRequested = false; + while (!exitThreadRequested) { - wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, - FALSE, - static_cast(_monarch.GetPID())) }; - // TODO:MG If we fail to open the monarch, then they don't exist - // anymore! Go straight to an election. - // - // TODO:MG At any point in all this, the current monarch might die. - // We go straight to a new election, right? Worst case, eventually, - // we'll become the new monarch. - // - // if (hMonarch.get() == nullptr) - // { - // const auto gle = GetLastError(); - // return false; - // } - waits[0] = hMonarch.get(); - auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); - - switch (waitResult) + // At any point in all this, the current monarch might die. If it + // does, we'll go straight to a new election, in the "jail" + // try/catch below. Worst case, eventually, we'll become the new + // monarch. + try { - case WAIT_OBJECT_0 + 0: // waits[0] was signaled - - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_MonarchDied", - TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // Connect to the new monarch, which might be us! - // If we become the monarch, then we'll return true and exit this thread. - exitRequested = _electionNight2020(); - break; - case WAIT_OBJECT_0 + 1: // waits[1] was signaled + // This might fail to even ask the monarch for it's PID. + wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, + FALSE, + static_cast(_monarch.GetPID())) }; + + // If we fail to open the monarch, then they don't exist + // anymore! Go straight to an election. + if (hMonarch.get() == nullptr) + { + const auto gle = GetLastError(); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_FailedToOpenMonarch", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + exitThreadRequested = _performElection(); + continue; + } + + waits[0] = hMonarch.get(); + auto waitResult = WaitForMultipleObjects(2, waits, FALSE, INFINITE); + + switch (waitResult) + { + case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the monarch process + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchDied", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // Connect to the new monarch, which might be us! + // If we become the monarch, then we'll return true and exit this thread. + exitThreadRequested = _performElection(); + break; + + case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt + + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchWaitInterrupted", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + exitThreadRequested = true; + break; + + case WAIT_TIMEOUT: + // This should be impossible. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_MonarchWaitTimeout", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + exitThreadRequested = true; + break; + + default: + { + // Returning any other value is invalid. Just die. + const auto gle = GetLastError(); + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_WaitFailed", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + ExitProcess(0); + } + } + } + catch (...) + { + // Theoretically, if window[1] dies when we're trying to get + // it's PID we'll get here. If we just try to do the election + // once here, it's possible we might elect window[2], but have + // it die before we add ourselves as a peasant. That + // _performElection call will throw, and we wouldn't catch it + // here, and we'd die. + + // Instead, we're going to have a resilent election process. + // We're going to keep trying an election, until one _doesn't_ + // throw an exception. That might mean burning through all the + // other dying monarchs until we find us as the monarch. But if + // this process is alive, then there's _someone_ in the line of + // succession. TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_MonarchWaitInterrupted", - TraceLoggingUInt64(_peasant.GetID(), "peasantID", "Our peasant ID"), + "WindowManager_ExceptionInWaitThread", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - exitRequested = true; - break; - - case WAIT_TIMEOUT: - printf("Wait timed out. This should be impossible.\n"); - exitRequested = true; - break; - - // Return value is invalid. - default: - { - auto gle = GetLastError(); - printf("WaitForMultipleObjects returned: %d\n", waitResult); - printf("Wait error: %d\n", gle); - ExitProcess(0); - } + bool foundNewMonarch = false; + while (!foundNewMonarch) + { + try + { + exitThreadRequested = _performElection(); + // It doesn't matter if we're the monarch, or someone + // else is, but if we complete the election, then we've + // registered with a new one. We can escape this jail + // and re-enter society. + foundNewMonarch = true; + } + catch (...) + { + // If we fail to acknowledge the results of the election, + // stay in this jail until we do. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ExceptionInNestedWaitThread", + TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } } } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 5e52210cb33..a577ab5e502 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -38,7 +38,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _areWeTheKing(); winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant(std::optional givenID); - bool _electionNight2020(); + bool _performElection(); void _createPeasantThread(); void _waitOnMonarchThread(); void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index dd635eecd1f..247061fcc72 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -543,6 +543,60 @@ namespace RemotingUnitTests void RemotingTests::ProposeCommandlineDeadWindow() { Log::Comment(L"Test proposing a commandline for a peasant that previously died"); - VERIFY_ARE_EQUAL(true, false, L"TODO: Finish this test"); + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& /*cmdlineArgs*/) { + Log::Comment(L"Commandline dispatched to p1"); + VERIFY_IS_TRUE(false, L"This should not happen, this peasant should be dead."); + }); + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + Log::Comment(L"Commandline dispatched to p2"); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); + }); + + std::vector p1Args{ L"1", L"arg[1]" }; + std::vector p2Args{ L"2", L"this is for p2" }; + + Log::Comment(L"Kill peasant 1"); + + _killPeasant(m0, 1); + + { + Log::Comment(L"Send a commandline to p2, who is still alive. We won't create a new window."); + + Remoting::CommandlineArgs eventArgs{ { p2Args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(false, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(false, (bool)result.Id()); + } + { + Log::Comment(L"Send a commandline to p1, who is dead. We will create a new window."); + Remoting::CommandlineArgs eventArgs{ { p1Args }, { L"" } }; + + auto result = m0->ProposeCommandline(eventArgs); + VERIFY_ARE_EQUAL(true, result.ShouldCreateWindow()); + VERIFY_ARE_EQUAL(true, (bool)result.Id()); + VERIFY_ARE_EQUAL(1u, result.Id().Value()); + } } } From b2db3170ddf3d8c254c859baf2f31de316d8a35f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 26 Jan 2021 11:51:06 -0600 Subject: [PATCH 40/76] man I can't spel --- .github/actions/spell-check/expect/web.txt | 1 + src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/Remoting/Monarch.idl | 2 +- src/cascadia/Remoting/WindowManager.cpp | 4 ++-- src/cascadia/TerminalApp/AppLogic.cpp | 6 +++--- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- src/cascadia/WindowsTerminal/main.cpp | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/actions/spell-check/expect/web.txt b/.github/actions/spell-check/expect/web.txt index c0c237ed13c..919e5e9267a 100644 --- a/.github/actions/spell-check/expect/web.txt +++ b/.github/actions/spell-check/expect/web.txt @@ -13,3 +13,4 @@ fixterms uk winui appshellintegration +cppreference diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index fa15636a72a..5d38fcfeb2e 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -129,7 +129,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count(); // For now, we'll just pay attention to whoever the most recent peasant - // was. We're not too wooried about the mru peasant dying. Worst case - + // was. We're not too worried about the mru peasant dying. Worst case - // when the user executes a `wt -w 0`, we won't be able to find that // peasant, and it'll open in a new window instead of the current one. if (args.ActivatedTime() > _lastActivatedTime) diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 206a48d05db..6434a6092ec 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -13,7 +13,7 @@ namespace Microsoft.Terminal.Remoting [default_interface] runtimeclass ProposeCommandlineResult { Windows.Foundation.IReference Id { get; }; - // TOOD:projects/5 - also return the name here, if the name was set on the commandline + // TODO:projects/5 - also return the name here, if the name was set on the commandline Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index df7006b3ca4..bcc927feaa9 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -167,7 +167,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } // Here, we're the king! // - // This is where you should do any aditional setup that might need to be + // This is where you should do any additional setup that might need to be // done when we become the king. THis will be called both for the first // window, and when the current monarch dies. @@ -327,7 +327,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // _performElection call will throw, and we wouldn't catch it // here, and we'd die. - // Instead, we're going to have a resilent election process. + // Instead, we're going to have a resilient election process. // We're going to keep trying an election, until one _doesn't_ // throw an exception. That might mean burning through all the // other dying monarchs until we find us as the monarch. But if diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 03fd9a05029..8b9c16d5279 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1162,7 +1162,7 @@ namespace winrt::TerminalApp::implementation // CWD. // Arguments: // - args: an array of strings to process as a commandline. These args can contain spaces - // - cwd: The directory to use as the CWD while performing thse actions. + // - cwd: The directory to use as the CWD while performing these actions. // Return Value: // - the result of the first command who's parsing returned a non-zero code, // or 0. (see AppLogic::_ParseArgs) @@ -1217,13 +1217,13 @@ namespace winrt::TerminalApp::implementation } // Any unsuccessful parse will be a new window. That new window will try - // to handle the commandline iteslf, and find that the commandline + // to handle the commandline itself, and find that the commandline // failed to parse. When that happens, the new window will display the // message box. // // This will also work for the case where the user specifies an invalid // commandline in conjunction with `-w 0`. This function will determine - // that the commandline hasa parse error, and indicate that we should + // that the commandline has a parse error, and indicate that we should // create a new window. Then, in that new window, we'll try to set the // StartupActions, which will again fail, returning the correct error // message. diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 406b08580e7..2a6ba0bc0d2 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -375,7 +375,7 @@ namespace winrt::TerminalApp::implementation std::wstring originalCwd{ wil::GetCurrentDirectoryW() }; auto restoreCwd = wil::scope_exit([&originalCwd]() { // ignore errors, we'll just power on through. We'd rather do - // something rather than fail silently if the direcotry doesn't + // something rather than fail silently if the directory doesn't // actually exist. LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(originalCwd.c_str())); }); @@ -386,7 +386,7 @@ namespace winrt::TerminalApp::implementation else { // ignore errors, we'll just power on through. We'd rather do - // something rather than fail silently if the direcotry doesn't + // something rather than fail silently if the directory doesn't // actually exist. LOG_IF_WIN32_BOOL_FALSE(SetCurrentDirectory(cwd.c_str())); } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6fa2794b192..e2e7703de63 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -538,7 +538,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se // manager will ask us how to figure out what the target window is for a set // of commandline arguments. We'll take those arguments, and ask AppLogic to // parse them for us. We'll then set ResultTargetWindow in the given args, so -// the sender can use that reuslt. +// the sender can use that result. // Arguments: // - args: the bundle of a commandline and working directory to find the correct target window for. // Return Value: diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index f763edd9d8e..0bf491de744 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -127,7 +127,7 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) if (!host.HasWindow()) { // If we were told to not have a window, exit early. Make sure to use - // ExitProcess to commit sepuku here. If you try just `return 0`, then + // ExitProcess to die here. If you try just `return 0`, then // the XAML app host will crash during teardown. ExitProcess avoids // that. ExitProcess(0); From d2a34389a85888b65cf19e1a71209f2b9433a99c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 27 Jan 2021 10:23:47 -0600 Subject: [PATCH 41/76] Add try/catch's throughout Monarch.cpp --- src/cascadia/Remoting/CommandlineArgs.cpp | 4 +- src/cascadia/Remoting/CommandlineArgs.h | 4 +- src/cascadia/Remoting/Monarch.cpp | 127 ++++++++++++++-------- src/cascadia/Remoting/Peasant.idl | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 6 +- 5 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/cascadia/Remoting/CommandlineArgs.cpp b/src/cascadia/Remoting/CommandlineArgs.cpp index db90f3381fd..4f200b7aa61 100644 --- a/src/cascadia/Remoting/CommandlineArgs.cpp +++ b/src/cascadia/Remoting/CommandlineArgs.cpp @@ -13,12 +13,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // It must be defined after CommandlineArgs.g.cpp, otherwise the compiler // will give you just the most impossible template errors to try and // decipher. - void CommandlineArgs::Args(winrt::array_view const& value) + void CommandlineArgs::Commandline(winrt::array_view const& value) { _args = { value.begin(), value.end() }; } - winrt::com_array CommandlineArgs::Args() + winrt::com_array CommandlineArgs::Commandline() { return winrt::com_array{ _args.begin(), _args.end() }; } diff --git a/src/cascadia/Remoting/CommandlineArgs.h b/src/cascadia/Remoting/CommandlineArgs.h index 159071854f2..d76830f65d4 100644 --- a/src/cascadia/Remoting/CommandlineArgs.h +++ b/src/cascadia/Remoting/CommandlineArgs.h @@ -23,8 +23,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::hstring CurrentDirectory() { return _cwd; }; - void Args(winrt::array_view const& value); - winrt::com_array Args(); + void Commandline(winrt::array_view const& value); + winrt::com_array Commandline(); private: winrt::com_array _args; diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 5d38fcfeb2e..08eb5913303 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -46,35 +46,48 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - the ID assigned to the peasant. uint64_t Monarch::AddPeasant(Remoting::IPeasant peasant) { - // TODO:projects/5 This is terrible. There's gotta be a better way - // of finding the first opening in a non-consecutive map of int->object - const auto providedID = peasant.GetID(); - - if (providedID == 0) - { - // Peasant doesn't currently have an ID. Assign it a new one. - peasant.AssignID(_nextPeasantID++); - } - else + try { - // Peasant already had an ID (from an older monarch). Leave that one - // be. Make sure that the next peasant's ID is higher than it. - _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; - } + // TODO:projects/5 This is terrible. There's gotta be a better way + // of finding the first opening in a non-consecutive map of int->object + const auto providedID = peasant.GetID(); - auto newPeasantsId = peasant.GetID(); - _peasants[newPeasantsId] = peasant; + if (providedID == 0) + { + // Peasant doesn't currently have an ID. Assign it a new one. + peasant.AssignID(_nextPeasantID++); + } + else + { + // Peasant already had an ID (from an older monarch). Leave that one + // be. Make sure that the next peasant's ID is higher than it. + _nextPeasantID = providedID >= _nextPeasantID ? providedID + 1 : _nextPeasantID; + } - // Add an event listener to the peasant's WindowActivated event. - peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); + auto newPeasantsId = peasant.GetID(); + // Add an event listener to the peasant's WindowActivated event. + peasant.WindowActivated({ this, &Monarch::_peasantWindowActivated }); - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_AddPeasant", - TraceLoggingUInt64(providedID, "providedID", "the provided ID for the peasant"), - TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + _peasants[newPeasantsId] = peasant; - return newPeasantsId; + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_AddPeasant", + TraceLoggingUInt64(providedID, "providedID", "the provided ID for the peasant"), + TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return newPeasantsId; + } + catch (...) + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_AddPeasant_Failed", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // We can only get into this try/catch if the peasant died on us. So + // the return value doesn't _really_ matter. They're not about to + // get it. + return -1; + } } // Method Description: @@ -104,6 +117,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto peasantSearch = _peasants.find(peasantID); auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; + // Ask the peasant for their PID. This will validate that they're + // actually still alive. if (maybeThePeasant) { maybeThePeasant.GetPID(); @@ -148,23 +163,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Monarch::_getMostRecentPeasantID() { - if (_mostRecentPeasant == 0) + if (_mostRecentPeasant != 0) { - // We haven't yet been told the MRU peasant. Just use the first one. - // This is just gonna be a random one, but really shouldn't happen - // in practice. The WindowManager should set the MRU peasant - // immediately as soon as it creates the monarch/peasant for the - // first window. - if (_peasants.size() > 0) + return _mostRecentPeasant; + } + + // We haven't yet been told the MRU peasant. Just use the first one. + // This is just gonna be a random one, but really shouldn't happen + // in practice. The WindowManager should set the MRU peasant + // immediately as soon as it creates the monarch/peasant for the + // first window. + if (_peasants.size() > 0) + { + try { return _peasants.begin()->second.GetID(); } - return 0; - } - else - { - return _mostRecentPeasant; + catch (...) + { + // This shouldn't really happen. If we're the monarch, then the + // first peasant should also _be us_. So we should be able to + // get our own ID. + return 0; + } } + return 0; } // Method Description: @@ -187,6 +210,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation auto findWindowArgs = winrt::make_self(); findWindowArgs->Args(args); + // This is handled by some handler in-proc _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); // After the event was handled, ResultTargetWindow() will be filled with @@ -205,20 +229,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (auto targetPeasant{ _getPeasant(windowID) }) { - // This will raise the peasant's ExecuteCommandlineRequested - // event, which will then ask the AppHost to handle the - // commandline, which will then pass it to AppLogic for - // handling. - targetPeasant.ExecuteCommandline(args); + auto result = winrt::make_self(); + + result->ShouldCreateWindow(false); + try + { + // This will raise the peasant's ExecuteCommandlineRequested + // event, which will then ask the AppHost to handle the + // commandline, which will then pass it to AppLogic for + // handling. + targetPeasant.ExecuteCommandline(args); + } + catch (...) + { + // If we fail to propose the commandline to the peasant (it + // died?) then just tell this process to become a new window + // instead. + result->ShouldCreateWindow(false); + } TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", - TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"), TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"), + TraceLoggingBoolean(!result->ShouldCreateWindow(), "succeeded", "true if we successfully dispatched the commandline to the peasant"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - auto result = winrt::make_self(); - result->ShouldCreateWindow(false); return *result; } else if (windowID > 0) @@ -229,7 +264,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", - TraceLoggingUInt64(windowID, "peasantID", "the ID of the matching peasant"), + TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"), TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index e3fb5af1b86..c77b5673e5c 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -9,7 +9,7 @@ namespace Microsoft.Terminal.Remoting CommandlineArgs(); CommandlineArgs(String[] args, String cwd); - String[] Args { get; set; }; + String[] Commandline { get; set; }; String CurrentDirectory(); }; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index e2e7703de63..6b678cae94c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -168,7 +168,7 @@ void AppHost::_HandleCommandlineArgs() { if (auto args{ peasant.InitialArgs() }) { - const auto result = _logic.SetStartupCommandline(args.Args()); + const auto result = _logic.SetStartupCommandline(args.Commandline()); const auto message = _logic.ParseCommandlineMessage(); if (!message.empty()) { @@ -530,7 +530,7 @@ bool AppHost::HasWindow() void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*sender*/, Remoting::CommandlineArgs args) { - _logic.ExecuteCommandline(args.Args(), args.CurrentDirectory()); + _logic.ExecuteCommandline(args.Commandline(), args.CurrentDirectory()); } // Method Description: @@ -546,7 +546,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable /*se void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& /*sender*/, const Remoting::FindTargetWindowArgs& args) { - const auto targetWindow = _logic.FindTargetWindow(args.Args().Args()); + const auto targetWindow = _logic.FindTargetWindow(args.Args().Commandline()); args.ResultTargetWindow(targetWindow); } From a65f3419e39db50b2ee24e5e7e55fdfb651fe270 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 27 Jan 2021 12:15:06 -0600 Subject: [PATCH 42/76] trycatch the window manager. A lot more going on there. --- src/cascadia/Remoting/WindowManager.cpp | 73 +++++++++++++++++++++---- src/cascadia/Remoting/WindowManager.h | 2 +- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index bcc927feaa9..de4eb40a826 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -23,7 +23,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Register with COM as a server for the Monarch class _registerAsMonarch(); // Instantiate an instance of the Monarch. This may or may not be in-proc! - _createMonarchAndCallbacks(); + bool foundMonarch = false; + while (!foundMonarch) + { + try + { + _createMonarchAndCallbacks(); + // _createMonarchAndCallbacks will initialize _isKing + foundMonarch = true; + } + catch (...) + { + // If we fail to find the monarch, + // stay in this jail until we do. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ExceptionInCtor", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } } WindowManager::~WindowManager() @@ -36,6 +53,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _registrationHostClass = 0; _monarchWaitInterrupt.SetEvent(); + // A thread is joinable once it's been started. Basically this just + // makes sure that the thread isn't just default-constructed. if (_electionThread.joinable()) { _electionThread.join(); @@ -44,15 +63,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) { - const bool isKing = _areWeTheKing(); // If we're the king, we _definitely_ want to process the arguments, we were // launched with them! // // Otherwise, the King will tell us if we should make a new window - _shouldCreateWindow = isKing; + _shouldCreateWindow = _isKing; std::optional givenID; - - if (!isKing) + if (!_isKing) { // The monarch may respond back "you should be a new // window, with ID,name of (id, name)". Really the responses are: @@ -106,7 +123,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _createOurPeasant({ givenID }); // Spawn a thread to wait on the monarch, and handle the election - if (!isKing) + if (!_isKing) { _createPeasantThread(); } @@ -144,15 +161,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation CLSCTX_LOCAL_SERVER); } + // NOTE: This can throw! Callers include: + // - the constructor, who performs this in a loop until it successfully + // finda a monarch + // - the performElection method, which is called in the waitOnMonarch + // thread. All the calls in that thread are wrapped in try/catch's + // already. + // - _createOurPeasant, who might do this in a loop to establish us with the + // monarch. void WindowManager::_createMonarchAndCallbacks() { _createMonarch(); - const auto isKing = _areWeTheKing(); + // Save the result of checking if we're the king. We want to avoid + // unnecessary calls back and forth if we can. + _isKing = _areWeTheKing(); TraceLoggingWrite(g_hRemotingProvider, "WindowManager_ConnectedToMonarch", TraceLoggingUInt64(_monarch.GetPID(), "monarchPID", "The PID of the new Monarch"), - TraceLoggingBoolean(isKing, "isKing", "true if we are the new monarch"), + TraceLoggingBoolean(_isKing, "isKing", "true if we are the new monarch"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); if (_peasant) @@ -161,7 +188,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _monarch.HandleActivatePeasant(_peasant.GetLastActivatedArgs()); } - if (!isKing) + if (!_isKing) { return; } @@ -176,8 +203,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::_areWeTheKing() { - const auto kingPID{ _monarch.GetPID() }; const auto ourPID{ GetCurrentProcessId() }; + const auto kingPID{ _monarch.GetPID() }; return (ourPID == kingPID); } @@ -189,7 +216,28 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation p->AssignID(givenID.value()); } _peasant = *p; - _monarch.AddPeasant(_peasant); + + // Try to add us to the monarch. If that fails, try to find a monarch + // again, until we find one (we will eventually find us) + while (true) + { + try + { + _monarch.AddPeasant(_peasant); + break; + } + catch (...) + { + try + { + // Wrap this in it's own try/catch, beause this can throw. + _createMonarchAndCallbacks(); + } + catch (...) + { + } + } + } TraceLoggingWrite(g_hRemotingProvider, "WindowManager_CreateOurPeasant", @@ -206,6 +254,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - // Return Value: // - true iff we're the new monarch process. + // NOTE: This can throw! bool WindowManager::_performElection() { _createMonarchAndCallbacks(); @@ -213,7 +262,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Tell the new monarch who we are. We might be that monarch! _monarch.AddPeasant(_peasant); - if (_areWeTheKing()) + if (_isKing) { // This method is only called when a _new_ monarch is elected. So // don't do anything here that needs to be done for all monarch diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index a577ab5e502..857b161485a 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -24,12 +24,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation private: bool _shouldCreateWindow{ false }; + bool _isKing{ false }; DWORD _registrationHostClass{ 0 }; winrt::Microsoft::Terminal::Remoting::Monarch _monarch{ nullptr }; winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr }; wil::unique_event _monarchWaitInterrupt; - std::thread _electionThread; void _registerAsMonarch(); From faf4544e9496e804858e3a0def41a9250b138a13 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 29 Jan 2021 11:25:56 -0600 Subject: [PATCH 43/76] add some abstracts --- src/cascadia/Remoting/FindTargetWindowArgs.h | 19 +++++++++++++++++-- .../Remoting/ProposeCommandlineResult.h | 19 +++++++++++++++++-- src/cascadia/Remoting/WindowActivatedArgs.h | 14 ++++++++++++-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.h b/src/cascadia/Remoting/FindTargetWindowArgs.h index 7a30b7b1815..aa86f5623e3 100644 --- a/src/cascadia/Remoting/FindTargetWindowArgs.h +++ b/src/cascadia/Remoting/FindTargetWindowArgs.h @@ -1,5 +1,20 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Class Name: +- FindTargetWindowArgs.h + +Abstract: +- This is a helper class for determining which window a specific commandline is + intended for. The Monarch will create one of these, then toss it over to + TerminalApp. TerminalApp actually contains the logic for parsing a + commandline, as well as settings like the windowing behavior. Once the + TerminalApp determines the correct window, it'll fill in the + ResultTargetWindow property. The monarch will then read that value out to + invoke the commandline in the appropriate window. + +--*/ #pragma once diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.h b/src/cascadia/Remoting/ProposeCommandlineResult.h index c45f134a1ac..adeedd87423 100644 --- a/src/cascadia/Remoting/ProposeCommandlineResult.h +++ b/src/cascadia/Remoting/ProposeCommandlineResult.h @@ -1,5 +1,20 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Class Name: +- ProposeCommandlineResult.h + +Abstract: +- This is a helper class for encapsulating the result of a + Monarch::ProposeCommandline call. The monarch will be telling the new process + whether it should create a new window or not. If the value of + ShouldCreateWindow is false, that implies that some other window process was + given the commandline for handling, and the caller should just exit. +- If ShouldCreateWindow is true, the Id property may or may not contain an ID + that the new window should use as it's ID. + +--*/ #pragma once diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h index 465dd6f8f2b..de663383648 100644 --- a/src/cascadia/Remoting/WindowActivatedArgs.h +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -1,6 +1,16 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +Class Name: +- WindowActivatedArgs.h + +Abstract: +- This is a helper class for encapsulating all the information about when and + where a window was activated. This will be used by the Monarch to determine + who the most recent peasant is. + +--*/ #pragma once #include "WindowActivatedArgs.g.h" From 19765b16964fab262f07b141af04cc3914af38cf Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 29 Jan 2021 11:29:23 -0600 Subject: [PATCH 44/76] Apply suggestions from code review Co-authored-by: Carlos Zamora --- src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 4 ++-- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 9 ++++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 08eb5913303..a1bab69ded6 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -115,7 +115,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { try { - auto peasantSearch = _peasants.find(peasantID); + const auto peasantSearch = _peasants.find(peasantID); auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second; // Ask the peasant for their PID. This will validate that they're // actually still alive. diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 8b9c16d5279..998611ccc3b 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1184,7 +1184,7 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Parse the given commandline args in an attempt to find the specified // window. The rest of the args are ignored for now (they'll be handled - // whenever the commandline fets to the window it was intended for). + // whenever the commandline gets to the window it was intended for). // - Note that this function will only ever be called by the monarch. A // return value of `0` in this case does not mean "run the commandline in // _this_ process", rather it means "run the commandline in the current @@ -1198,7 +1198,7 @@ namespace winrt::TerminalApp::implementation int32_t AppLogic::FindTargetWindow(array_view args) { ::TerminalApp::AppCommandlineArgs appArgs; - auto result = appArgs.ParseArgs(args); + const auto result = appArgs.ParseArgs(args); if (result == 0) { return appArgs.GetTargetWindow(); diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 247061fcc72..f9c55750047 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -36,7 +36,7 @@ using namespace winrt::Microsoft::Terminal; namespace RemotingUnitTests { // This is a silly helper struct. - // It will always throw an hresult_error on any of it's methods. + // It will always throw an hresult_error on any of its methods. // // In the tests, it's hard to emulate a peasant process being totally dead // once the Monarch has captured a reference to it. Since everything's @@ -112,10 +112,10 @@ namespace RemotingUnitTests void RemotingTests::_findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) { - auto arguments = args.Args().Args(); + const auto arguments = args.Args().Args(); if (arguments.size() > 0) { - auto index = std::stoi(arguments.at(0).c_str()); + const auto index = std::stoi(arguments.at(0).c_str()); args.ResultTargetWindow(index); } } @@ -424,6 +424,7 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(false, (bool)result.Id()); } } + void RemotingTests::ProposeCommandlineCurrentWindow() { Log::Comment(L"Test proposing a commandline for the current window (ID=0)"); @@ -504,6 +505,7 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(false, (bool)result.Id()); } } + void RemotingTests::ProposeCommandlineNonExistentWindow() { Log::Comment(L"Test proposing a commandline for an ID that doesn't have a current peasant"); @@ -540,6 +542,7 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(10u, result.Id().Value()); } } + void RemotingTests::ProposeCommandlineDeadWindow() { Log::Comment(L"Test proposing a commandline for a peasant that previously died"); From 4e007ae15129a0b9a2ad9b8bcbdbd69e466ad989 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 29 Jan 2021 12:03:06 -0600 Subject: [PATCH 45/76] a ton of PR feedback --- src/cascadia/Remoting/FindTargetWindowArgs.h | 6 ++++- src/cascadia/Remoting/Monarch.cpp | 18 +++++++-------- src/cascadia/Remoting/Monarch.idl | 6 ++++- src/cascadia/Remoting/Peasant.cpp | 2 +- .../Remoting/ProposeCommandlineResult.h | 5 +++++ src/cascadia/Remoting/WindowManager.h | 22 +++++++++++++++++-- .../TerminalApp/AppCommandlineArgs.cpp | 11 +++------- src/cascadia/TerminalApp/TerminalPage.cpp | 3 +++ 8 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.h b/src/cascadia/Remoting/FindTargetWindowArgs.h index aa86f5623e3..9dd4c263083 100644 --- a/src/cascadia/Remoting/FindTargetWindowArgs.h +++ b/src/cascadia/Remoting/FindTargetWindowArgs.h @@ -25,8 +25,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { struct FindTargetWindowArgs : public FindTargetWindowArgsT { - public: GETSET_PROPERTY(winrt::Microsoft::Terminal::Remoting::CommandlineArgs, Args, nullptr); GETSET_PROPERTY(int, ResultTargetWindow, -1); + + public: + FindTargetWindowArgs(winrt::Microsoft::Terminal::Remoting::CommandlineArgs args) : + _Args{ args } {}; }; } +BASIC_FACTORY(FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index a1bab69ded6..a5c3a740f64 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -207,8 +207,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // dependency hell). The WindowManager will raise this up to the app // host, which will then ask the AppLogic, who will then parse the // commandline and determine the provided ID of the window. - auto findWindowArgs = winrt::make_self(); - findWindowArgs->Args(args); + auto findWindowArgs{ winrt::make_self(args) }; // This is handled by some handler in-proc _FindTargetWindowRequestedHandlers(*this, *findWindowArgs); @@ -229,9 +228,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (auto targetPeasant{ _getPeasant(windowID) }) { - auto result = winrt::make_self(); + auto result{ winrt::make_self(false) }; - result->ShouldCreateWindow(false); try { // This will raise the peasant's ExecuteCommandlineRequested @@ -245,7 +243,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // If we fail to propose the commandline to the peasant (it // died?) then just tell this process to become a new window // instead. - result->ShouldCreateWindow(false); + result->ShouldCreateWindow(true); + + // If this fails, it'll be logged in the following + // TraceLoggingWrite statement, with succeeded=false } TraceLoggingWrite(g_hRemotingProvider, @@ -268,8 +269,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - auto result = winrt::make_self(); - result->ShouldCreateWindow(true); + auto result{ winrt::make_self(true) }; result->Id(windowID); return *result; } @@ -281,9 +281,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // In this case, no usable ID was provided. Return { true, nullopt } - auto result = winrt::make_self(); - result->ShouldCreateWindow(true); - return *result; + return winrt::make(true); } } diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 6434a6092ec..43bbbeda1ab 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -7,11 +7,15 @@ namespace Microsoft.Terminal.Remoting { [default_interface] runtimeclass FindTargetWindowArgs { - CommandlineArgs Args; + FindTargetWindowArgs(CommandlineArgs args); + + CommandlineArgs Args { get; }; Int32 ResultTargetWindow; } [default_interface] runtimeclass ProposeCommandlineResult { + ProposeCommandlineResult(Boolean shouldCreateWindow); + Windows.Foundation.IReference Id { get; }; // TODO:projects/5 - also return the name here, if the name was set on the commandline Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode diff --git a/src/cascadia/Remoting/Peasant.cpp b/src/cascadia/Remoting/Peasant.cpp index 699adf976b2..ee34887c2a9 100644 --- a/src/cascadia/Remoting/Peasant.cpp +++ b/src/cascadia/Remoting/Peasant.cpp @@ -87,7 +87,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // Try/catch this, because the other side of this event is handled // by the monarch. The monarch might have died. If they have, this - // will throw and exception. Just eat it, the election thread will + // will throw an exception. Just eat it, the election thread will // handle hooking up the new one. _WindowActivatedHandlers(*this, args); successfullyNotified = true; diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.h b/src/cascadia/Remoting/ProposeCommandlineResult.h index adeedd87423..4ae54f866f9 100644 --- a/src/cascadia/Remoting/ProposeCommandlineResult.h +++ b/src/cascadia/Remoting/ProposeCommandlineResult.h @@ -28,5 +28,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation public: GETSET_PROPERTY(Windows::Foundation::IReference, Id); GETSET_PROPERTY(bool, ShouldCreateWindow, true); + + public: + ProposeCommandlineResult(bool shouldCreateWindow) : + _ShouldCreateWindow{ shouldCreateWindow } {}; }; } +BASIC_FACTORY(ProposeCommandlineResult); diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 857b161485a..634e297701b 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -1,5 +1,23 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Class Name: +- ProposeCommandlineResult.h + +Abstract: +- The Window Manager takes care of coordinating the monarch and peasant for this + process. +- It's responsible for registering as a potential future monarch. It's also + responsible for creating the Peasant for this process when it's determined + this process should become a window process. +- If we aren't the monarch, it's responsible for watching the current monarch + process, and finding the new one if the current monarch dies. +- When the monarch needs to ask the TerminalApp about how to parse a + commandline, it'll ask by raising an event that we'll bubble up to the + AppHost. + +--*/ #pragma once diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 3fc1fedb212..2fb64c38c59 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -185,14 +185,9 @@ void AppCommandlineArgs::_buildParser() maximized->excludes(fullscreen); focus->excludes(fullscreen); - auto* windowOpt = _app.add_option("-w,--window", - _windowTarget, - RS_A(L"CmdWindowTargetArgDesc")); - windowOpt; - - // maximized->excludes(windowOpt); - // focus->excludes(windowOpt); - // fullscreen->excludes(windowOpt); + _app.add_option("-w,--window", + _windowTarget, + RS_A(L"CmdWindowTargetArgDesc")); // Subcommands _buildNewTabParser(); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 2a6ba0bc0d2..de53c03ab8b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -352,6 +352,9 @@ namespace winrt::TerminalApp::implementation // other side of the co_await. // - initial: if true, we're parsing these args during startup, and we // should fire an Initialized event. + // - cwd: If not empty, we should try switching to this provided directory + // while processing these actions. This will allow something like `wt -w 0 + // nt -d .` from inside another directory to work as expected. // Return Value: // - winrt::fire_and_forget TerminalPage::ProcessStartupActions(Windows::Foundation::Collections::IVector actions, From c493607cae5680241348a0f1ada88b87f981b7f4 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Feb 2021 07:28:19 -0600 Subject: [PATCH 46/76] bunch of PR feedback --- src/cascadia/Remoting/FindTargetWindowArgs.h | 1 - src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/Remoting/Monarch.idl | 4 --- .../Remoting/ProposeCommandlineResult.h | 1 - src/cascadia/Remoting/WindowManager.cpp | 29 ++++++++++--------- src/cascadia/Remoting/WindowManager.h | 2 +- 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/cascadia/Remoting/FindTargetWindowArgs.h b/src/cascadia/Remoting/FindTargetWindowArgs.h index 9dd4c263083..628004aa212 100644 --- a/src/cascadia/Remoting/FindTargetWindowArgs.h +++ b/src/cascadia/Remoting/FindTargetWindowArgs.h @@ -33,4 +33,3 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _Args{ args } {}; }; } -BASIC_FACTORY(FindTargetWindowArgs); diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index a5c3a740f64..1c3d1538906 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -281,7 +281,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // In this case, no usable ID was provided. Return { true, nullopt } - return winrt::make(true); + return winrt::make(true); } } diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index 43bbbeda1ab..3d44666b0f1 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -7,15 +7,11 @@ namespace Microsoft.Terminal.Remoting { [default_interface] runtimeclass FindTargetWindowArgs { - FindTargetWindowArgs(CommandlineArgs args); - CommandlineArgs Args { get; }; Int32 ResultTargetWindow; } [default_interface] runtimeclass ProposeCommandlineResult { - ProposeCommandlineResult(Boolean shouldCreateWindow); - Windows.Foundation.IReference Id { get; }; // TODO:projects/5 - also return the name here, if the name was set on the commandline Boolean ShouldCreateWindow { get; }; // If you name this `CreateWindow`, the compiler will explode diff --git a/src/cascadia/Remoting/ProposeCommandlineResult.h b/src/cascadia/Remoting/ProposeCommandlineResult.h index 4ae54f866f9..39b1cd6071b 100644 --- a/src/cascadia/Remoting/ProposeCommandlineResult.h +++ b/src/cascadia/Remoting/ProposeCommandlineResult.h @@ -34,4 +34,3 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _ShouldCreateWindow{ shouldCreateWindow } {}; }; } -BASIC_FACTORY(ProposeCommandlineResult); diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index de4eb40a826..01a422a690d 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -80,7 +80,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - This is the case where the user provides `wt -w 1`, and // there's no existing window 1 - auto result = _monarch.ProposeCommandline(args); + const auto result = _monarch.ProposeCommandline(args); _shouldCreateWindow = result.ShouldCreateWindow(); if (result.Id()) { @@ -163,7 +163,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // NOTE: This can throw! Callers include: // - the constructor, who performs this in a loop until it successfully - // finda a monarch + // find a a monarch // - the performElection method, which is called in the waitOnMonarch // thread. All the calls in that thread are wrapped in try/catch's // already. @@ -230,7 +230,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { try { - // Wrap this in it's own try/catch, beause this can throw. + // Wrap this in it's own try/catch, because this can throw. _createMonarchAndCallbacks(); } catch (...) @@ -262,15 +262,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Tell the new monarch who we are. We might be that monarch! _monarch.AddPeasant(_peasant); - if (_isKing) - { - // This method is only called when a _new_ monarch is elected. So - // don't do anything here that needs to be done for all monarch - // windows. This should only be for work that's done when a window - // _becomes_ a monarch, after the death of the previous monarch. - return true; - } - return false; + // This method is only called when a _new_ monarch is elected. So + // don't do anything here that needs to be done for all monarch + // windows. This should only be for work that's done when a window + // _becomes_ a monarch, after the death of the previous monarch. + return _isKing; } void WindowManager::_createPeasantThread() @@ -287,9 +283,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void WindowManager::_waitOnMonarchThread() { + // This is the array of HANDLEs that we're going to wait on in + // WaitForMultipleObjects below. + // * waits[0] will be the handle to the monarch process. It gets + // signalled when the process exits / dies. + // * waits[1] is the handle to our _monarchWaitInterrupt event. Another + // thread can use that to manually break this loop. We'll do that when + // we're getting torn down. HANDLE waits[2]; waits[1] = _monarchWaitInterrupt.get(); - const auto peasantID = _peasant.GetID(); + const auto peasantID = _peasant.GetID(); // safe: _peasant is in-proc. bool exitThreadRequested = false; while (!exitThreadRequested) diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 634e297701b..bc257ef06b6 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation Licensed under the MIT license. Class Name: -- ProposeCommandlineResult.h +- WindowManager.h Abstract: - The Window Manager takes care of coordinating the monarch and peasant for this From e6bd3152f32327eb6752ca04711bdee682a74670 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 12:24:09 -0600 Subject: [PATCH 47/76] !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! (cherry picked from commit a41bee653ba0c66ebf5cf62135118e6fed7aefa7) --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index db8f5cd8cf9..cc24255e0eb 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -377,6 +377,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize { _WindowActivatedHandlers(); } + break; } case WM_NCLBUTTONDOWN: From a62d53fa45569dde84b3f8e49c2f096f6cb3af3e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 12:24:52 -0600 Subject: [PATCH 48/76] !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! (cherry picked from commit 6e7ea615d2fab114fce69907caf0777e5b8b7b72) --- src/cascadia/WindowsTerminal/AppHost.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 6b678cae94c..bc45ea9605a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -550,8 +550,10 @@ void AppHost::_FindTargetWindow(const winrt::Windows::Foundation::IInspectable& args.ResultTargetWindow(targetWindow); } -void AppHost::_WindowActivated() +winrt::fire_and_forget AppHost::_WindowActivated() { + co_await winrt::resume_background(); + if (auto peasant{ _windowManager.CurrentWindow() }) { // TODO: projects/5 - in the future, we'll want to actually get the From 147b187da9e19aafc4f3812748162c25abfbedfb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 28 Jan 2021 14:25:04 -0600 Subject: [PATCH 49/76] !!! THIS NEEDS TO BE MOVED TO THE PARENT BRANCH !!! > The **low-order word** specifies whether the window is being activated or deactivated. (cherry picked from commit 91b52d4e6dad210abf9fe8a8d7bb3a3e50f60e30) --- src/cascadia/WindowsTerminal/IslandWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index cc24255e0eb..4e45e91cada 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -373,7 +373,7 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize case WM_ACTIVATE: { // wparam = 0 indicates the window was deactivated - if (wparam != 0) + if (LOWORD(wparam) != 0) { _WindowActivatedHandlers(); } From a102f445285f70f3fa9d39c5f2698157714abbd6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Feb 2021 07:48:37 -0600 Subject: [PATCH 50/76] bad merge --- src/cascadia/WindowsTerminal/AppHost.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index b3baf25bbc1..db32ad67106 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -44,7 +44,7 @@ class AppHost void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& arg); void _WindowMouseWheeled(const til::point coord, const int32_t delta); - void _WindowActivated(); + winrt::fire_and_forget _WindowActivated(); void _DispatchCommandline(winrt::Windows::Foundation::IInspectable sender, winrt::Microsoft::Terminal::Remoting::CommandlineArgs args); From 7097274a042a395138c887eb143ada745b95b26f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Feb 2021 10:38:09 -0600 Subject: [PATCH 51/76] Thats right, did it in one commit suckas --- src/cascadia/Remoting/Monarch.cpp | 18 +++++++++-- .../TerminalApp/AppCommandlineArgs.cpp | 16 +++++++--- src/cascadia/TerminalApp/AppCommandlineArgs.h | 4 +-- src/cascadia/TerminalApp/AppLogic.cpp | 31 ++++++++++++++++++- .../GlobalAppSettings.cpp | 3 ++ .../TerminalSettingsModel/GlobalAppSettings.h | 1 + .../GlobalAppSettings.idl | 11 +++++++ .../TerminalSettingsSerializationHelpers.h | 9 ++++++ 8 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 1c3d1538906..d19c1434cda 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -216,10 +216,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // the parsed result. const auto targetWindow = findWindowArgs->ResultTargetWindow(); + auto tmp = targetWindow; + switch (targetWindow) + { + case -1: + tmp = -1; + break; + case -2: // UseExistingSameDesktop + tmp = 0; // TODO:MG for now, just use the MRU window. + break; + case -3: // UseExisting + tmp = 0; // TODO:MG for now, just use the MRU window. + break; + } + // If there's a valid ID returned, then let's try and find the peasant that goes with it. - if (targetWindow >= 0) + if (tmp >= 0) { - uint64_t windowID = ::base::saturated_cast(targetWindow); + uint64_t windowID = ::base::saturated_cast(tmp); if (windowID == 0) { diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 2fb64c38c59..c6782252125 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -185,9 +185,9 @@ void AppCommandlineArgs::_buildParser() maximized->excludes(fullscreen); focus->excludes(fullscreen); - _app.add_option("-w,--window", - _windowTarget, - RS_A(L"CmdWindowTargetArgDesc")); + _app.add_option, int>("-w,--window", + _windowTarget, + RS_A(L"CmdWindowTargetArgDesc")); // Subcommands _buildNewTabParser(); @@ -854,10 +854,16 @@ void AppCommandlineArgs::FullResetState() _exitMessage = ""; _shouldExitEarly = false; - _windowTarget = -1; + _windowTarget = std::nullopt; } -int AppCommandlineArgs::GetTargetWindow() const noexcept +std::optional AppCommandlineArgs::GetTargetWindow() const noexcept { + // If the user provides _any_ negative number, then treat it as -1, for "use a new window". + if (_windowTarget.has_value() && *_windowTarget < 0) + { + return { -1 }; + } + return _windowTarget; } diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index fa4c94ec2a1..a09bd36280b 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -44,7 +44,7 @@ class TerminalApp::AppCommandlineArgs final void DisableHelpInExitMessage(); void FullResetState(); - int GetTargetWindow() const noexcept; + std::optional GetTargetWindow() const noexcept; private: static const std::wregex _commandDelimiterRegex; @@ -106,7 +106,7 @@ class TerminalApp::AppCommandlineArgs final std::string _exitMessage; bool _shouldExitEarly{ false }; - int _windowTarget{ -1 }; + std::optional _windowTarget{ std::nullopt }; // Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand); diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 998611ccc3b..4e7024b4426 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1194,6 +1194,8 @@ namespace winrt::TerminalApp::implementation // Return Value: // - 0: We should handle the args "in the current window". // - -1: We should handle the args in a new window + // - -2: We should handle the args "in the current window ON THIS DESKTOP" + // - -3: We should handle the args "in the current window ON ANY DESKTOP" // - anything else: We should handle the commandline in the window with the given ID. int32_t AppLogic::FindTargetWindow(array_view args) { @@ -1201,7 +1203,34 @@ namespace winrt::TerminalApp::implementation const auto result = appArgs.ParseArgs(args); if (result == 0) { - return appArgs.GetTargetWindow(); + const auto parsedTarget = appArgs.GetTargetWindow(); + if (parsedTarget.has_value()) + { + // parsedTarget might be -1, if the user explicitly requested -1 + // (or any other negative number) on the commandline. So the set + // of possible values here is {-1, 0, ℤ+} + return *parsedTarget; + } + else + { + // If the user did not provide any value on the commandline, + // then lookup our windowing behavior to determine what to do + // now. + const auto windowingBehavior = _settings.GlobalSettings().WindowingBehavior(); + switch (windowingBehavior) + { + case WindowingMode::UseNew: + return -1; + case WindowingMode::UseExistingSameDesktop: + return -2; + case WindowingMode::UseExisting: + return -3; + default: + return -1; + } + } + + // return; // TODO:projects/5 // diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 4d2d904f878..77ee9406570 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -41,6 +41,7 @@ static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" }; static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" }; static constexpr std::string_view StartupActionsKey{ "startupActions" }; +static constexpr std::string_view WindowingBehaviorKey{ "windowingBehavior" }; static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" }; @@ -304,6 +305,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, StartupActionsKey, _StartupActions); + JsonUtils::GetValueForKey(json, WindowingBehaviorKey, _WindowingBehavior); + // This is a helper lambda to get the keybindings and commands out of both // and array of objects. We'll use this twice, once on the legacy // `keybindings` key, and again on the newer `bindings` key. diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index 8e1e65942b4..3998df35fca 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -87,6 +87,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GETSET_SETTING(Model::TabSwitcherMode, TabSwitcherMode, Model::TabSwitcherMode::InOrder); GETSET_SETTING(bool, DisableAnimations, false); GETSET_SETTING(hstring, StartupActions, L""); + GETSET_SETTING(Model::WindowingMode, WindowingBehavior, Model::WindowingMode::UseNew); private: guid _defaultProfile; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 6f0fc41dac1..9c62db04e81 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -33,6 +33,13 @@ namespace Microsoft.Terminal.Settings.Model Disabled, }; + enum WindowingMode + { + UseNew, + UseExisting, + UseExistingSameDesktop, + }; + [default_interface] runtimeclass GlobalAppSettings { Guid DefaultProfile; Boolean HasUnparsedDefaultProfile(); @@ -146,5 +153,9 @@ namespace Microsoft.Terminal.Settings.Model Boolean HasStartupActions(); void ClearStartupActions(); String StartupActions(); + + Boolean HasWindowingBehavior(); + void ClearWindowingBehavior(); + WindowingMode WindowingBehavior(); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 82b16cdefb0..397f68155a3 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -431,3 +431,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::CommandPaletteLa pair_type{ "commandLine", ValueType::CommandLine }, }; }; + +JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode) +{ + JSON_MAPPINGS(3) = { + pair_type{ "useNew", ValueType::UseNew }, + pair_type{ "useExisting", ValueType::UseExisting }, + pair_type{ "useExistingSameDesktop", ValueType::UseExistingSameDesktop }, + }; +}; From 67b525ba77774e6a180f4c19a094664006b1e7d7 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Feb 2021 12:41:53 -0600 Subject: [PATCH 52/76] barf I hate the stl --- src/cascadia/Remoting/Monarch.cpp | 63 +++++++++++++++++---- src/cascadia/Remoting/Monarch.h | 13 ++--- src/cascadia/Remoting/WindowActivatedArgs.h | 9 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 13 ++++- 4 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index d19c1434cda..5c24bf12068 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -5,6 +5,7 @@ #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" +#include "WindowActivatedArgs.h" #include "ProposeCommandlineResult.h" #include "Monarch.g.cpp" @@ -140,33 +141,73 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // peasant was the most recent per-desktop. When we want to get the most // recent of all desktops (WindowingBehavior::UseExisting), then use the // most recent of all desktops. - const auto oldLastActiveTime = _lastActivatedTime.time_since_epoch().count(); + // const auto oldLastActiveTime = _lastActivatedTime.time_since_epoch().count(); const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count(); - // For now, we'll just pay attention to whoever the most recent peasant - // was. We're not too worried about the mru peasant dying. Worst case - - // when the user executes a `wt -w 0`, we won't be able to find that - // peasant, and it'll open in a new window instead of the current one. - if (args.ActivatedTime() > _lastActivatedTime) + // * Check all the current lists to look for this peasant. + // remove it from any where it exists. + for (auto& [g, vec] : _mruPeasants) { - _mostRecentPeasant = args.PeasantID(); - _lastActivatedTime = args.ActivatedTime(); + auto result = std::find_if(vec.begin(), + vec.end(), + [args](auto other) { return args.PeasantID() == other.PeasantID(); }); + if (result != std::end(vec)) + { + vec.erase(result); + } + std::make_heap(vec.begin(), vec.end(), Remoting::implementation::CompareWindowActivatedArgs()); + } + // * If the current desktop doesn't have a vector, add one. + if (_mruPeasants.find(args.DesktopID()) == _mruPeasants.end()) + { + _mruPeasants[args.DesktopID()] = std::vector(); } + // * Add this args to the queue for the given desktop. + _mruPeasants[args.DesktopID()].push_back(args); + std::push_heap(_mruPeasants[args.DesktopID()].begin(), + _mruPeasants[args.DesktopID()].end(), + Remoting::implementation::CompareWindowActivatedArgs()); + + // // For now, we'll just pay attention to whoever the most recent peasant + // // was. We're not too worried about the mru peasant dying. Worst case - + // // when the user executes a `wt -w 0`, we won't be able to find that + // // peasant, and it'll open in a new window instead of the current one. + // if (args.ActivatedTime() > _lastActivatedTime) + // { + // _mostRecentPeasant = args.PeasantID(); + // _lastActivatedTime = args.ActivatedTime(); + // } TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"), - TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"), + // TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"), TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided args.ActivatedTime()"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } uint64_t Monarch::_getMostRecentPeasantID() { - if (_mostRecentPeasant != 0) + std::vector _mrus; + for (auto& [g, vec] : _mruPeasants) + { + if (vec.size() > 0) + { + // _mrus.push_heap(*vec.begin(), implementation::WindowActivatedArgs::compare); + _mrus.push_back(*vec.begin()); + std::push_heap(_mrus.begin(), + _mrus.end(), + Remoting::implementation::CompareWindowActivatedArgs()); + } + } + if (!_mrus.empty()) { - return _mostRecentPeasant; + return _mrus.begin()->PeasantID(); } + // if (_mostRecentPeasant != 0) + // { + // return _mostRecentPeasant; + // } // We haven't yet been told the MRU peasant. Just use the first one. // This is just gonna be a random one, but really shouldn't happen diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 96456580e88..4ec0ef115d8 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -30,12 +30,6 @@ constexpr GUID Monarch_clsid } }; -enum class WindowingBehavior : uint64_t -{ - UseNew = 0, - UseExisting = 1, -}; - namespace RemotingUnitTests { class RemotingTests; @@ -63,12 +57,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t _nextPeasantID{ 1 }; uint64_t _thisPeasantID{ 0 }; - uint64_t _mostRecentPeasant{ 0 }; - winrt::Windows::Foundation::DateTime _lastActivatedTime{}; + // uint64_t _mostRecentPeasant{ 0 }; + // winrt::Windows::Foundation::DateTime _lastActivatedTime{}; - WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew }; std::unordered_map _peasants; + std::unordered_map> _mruPeasants; + winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); uint64_t _getMostRecentPeasantID(); diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h index de663383648..03ee130705d 100644 --- a/src/cascadia/Remoting/WindowActivatedArgs.h +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -18,6 +18,13 @@ Class Name: namespace winrt::Microsoft::Terminal::Remoting::implementation { + struct CompareWindowActivatedArgs + { + bool operator()(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) const + { + return lhs.ActivatedTime() < rhs.ActivatedTime(); + } + }; struct WindowActivatedArgs : public WindowActivatedArgsT { GETSET_PROPERTY(uint64_t, PeasantID, 0); @@ -29,6 +36,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _PeasantID{ peasantID }, _DesktopID{ desktopID }, _ActivatedTime{ timestamp } {}; + static bool compare(const WindowActivatedArgs& lhs, const WindowActivatedArgs& rhs) { return lhs._ActivatedTime < rhs._ActivatedTime; } + static bool compare(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) { return lhs.ActivatedTime() < rhs.ActivatedTime(); } }; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index bc45ea9605a..121ecc0ee24 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -556,11 +556,22 @@ winrt::fire_and_forget AppHost::_WindowActivated() if (auto peasant{ _windowManager.CurrentWindow() }) { + GUID currentDesktopGuid{ 0 }; + try + { + auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + if (manager) + { + LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid)); + } + } + CATCH_LOG(); + // TODO: projects/5 - in the future, we'll want to actually get the // desktop GUID in IslandWindow, and bubble that up here, then down to // the Peasant. For now, we're just leaving space for it. Remoting::WindowActivatedArgs args{ peasant.GetID(), - winrt::guid{}, + currentDesktopGuid, winrt::clock().now() }; peasant.ActivateWindow(args); } From e4357c5825763051ec37dd488590c0e655319517 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Feb 2021 15:36:40 -0600 Subject: [PATCH 53/76] I'm not happy with this code, but i'm VERY HAPPY with how well it works --- src/cascadia/Remoting/Monarch.cpp | 97 +++++++++++++++------ src/cascadia/Remoting/Monarch.h | 2 +- src/cascadia/Remoting/Peasant.idl | 3 + src/cascadia/Remoting/WindowActivatedArgs.h | 14 ++- src/cascadia/Remoting/pch.h | 8 +- src/cascadia/WindowsTerminal/AppHost.cpp | 27 +++--- src/cascadia/WindowsTerminal/AppHost.h | 1 + 7 files changed, 111 insertions(+), 41 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 5c24bf12068..9c32ef5695c 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -186,24 +186,58 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - uint64_t Monarch::_getMostRecentPeasantID() + uint64_t Monarch::_getMostRecentPeasantID(bool limitToCurrentDesktop) { + winrt::com_ptr manager{ nullptr }; + if (limitToCurrentDesktop) + { + try + { + manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + } + CATCH_LOG(); + } + std::vector _mrus; for (auto& [g, vec] : _mruPeasants) { if (vec.size() > 0) { - // _mrus.push_heap(*vec.begin(), implementation::WindowActivatedArgs::compare); - _mrus.push_back(*vec.begin()); - std::push_heap(_mrus.begin(), - _mrus.end(), - Remoting::implementation::CompareWindowActivatedArgs()); + if (limitToCurrentDesktop && manager) + { + for (const auto& args : vec) + { + BOOL onCurrentDesktop{ false }; + // SUCCEEDED_LOG will log if it failed, and + // return true if it SUCCEEDED + if (SUCCEEDED_LOG(manager->IsWindowOnCurrentVirtualDesktop((HWND)args.Hwnd(), &onCurrentDesktop)) && + onCurrentDesktop) + { + _mrus.push_back(args); + std::push_heap(_mrus.begin(), + _mrus.end(), + Remoting::implementation::CompareWindowActivatedArgs()); + break; + } + } + } + else + { + _mrus.push_back(*vec.begin()); + std::push_heap(_mrus.begin(), + _mrus.end(), + Remoting::implementation::CompareWindowActivatedArgs()); + } } } if (!_mrus.empty()) { return _mrus.begin()->PeasantID(); } + else if (limitToCurrentDesktop) + { + return 0; + } // if (_mostRecentPeasant != 0) // { // return _mostRecentPeasant; @@ -214,7 +248,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // in practice. The WindowManager should set the MRU peasant // immediately as soon as it creates the monarch/peasant for the // first window. - if (_peasants.size() > 0) + else if (_peasants.size() > 0) { try { @@ -257,30 +291,43 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // the parsed result. const auto targetWindow = findWindowArgs->ResultTargetWindow(); - auto tmp = targetWindow; - switch (targetWindow) - { - case -1: - tmp = -1; - break; - case -2: // UseExistingSameDesktop - tmp = 0; // TODO:MG for now, just use the MRU window. - break; - case -3: // UseExisting - tmp = 0; // TODO:MG for now, just use the MRU window. - break; - } + // auto tmp = targetWindow; + // switch (targetWindow) + // { + // case -1: + // tmp = -1; + // break; + // case -2: // UseExistingSameDesktop + // tmp = _getMostRecentPeasantID(true); // TODO:MG for now, just use the MRU window. + // break; + // case -3: // UseExisting + // tmp = _getMostRecentPeasantID(false); // TODO:MG for now, just use the MRU window. + // break; + // } // If there's a valid ID returned, then let's try and find the peasant that goes with it. - if (tmp >= 0) + if (targetWindow >= 0 || targetWindow == -2 || targetWindow == -3) { - uint64_t windowID = ::base::saturated_cast(tmp); - - if (windowID == 0) + uint64_t windowID = 0; + switch (targetWindow) { - windowID = _getMostRecentPeasantID(); + case -2: // UseExistingSameDesktop + windowID = _getMostRecentPeasantID(true); // TODO:MG for now, just use the MRU window. + break; + case 0: // UseExisting + case -3: // UseExisting + windowID = _getMostRecentPeasantID(false); // TODO:MG for now, just use the MRU window. + break; + default: + windowID = ::base::saturated_cast(targetWindow); + break; } + // if (windowID == 0) + // { + // windowID = _getMostRecentPeasantID(false); + // } + if (auto targetPeasant{ _getPeasant(windowID) }) { auto result{ winrt::make_self(false) }; diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 4ec0ef115d8..5394039d0bd 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -65,7 +65,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation std::unordered_map> _mruPeasants; winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); - uint64_t _getMostRecentPeasantID(); + uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index c77b5673e5c..71f090bf93d 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -11,12 +11,15 @@ namespace Microsoft.Terminal.Remoting String[] Commandline { get; set; }; String CurrentDirectory(); + }; runtimeclass WindowActivatedArgs { WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime); + WindowActivatedArgs(UInt64 peasantID, UInt64 hwnd, Guid desktopID, Windows.Foundation.DateTime activatedTime); UInt64 PeasantID { get; }; + UInt64 Hwnd { get; }; Guid DesktopID { get; }; Windows.Foundation.DateTime ActivatedTime { get; }; }; diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h index 03ee130705d..d00a645c95d 100644 --- a/src/cascadia/Remoting/WindowActivatedArgs.h +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -30,14 +30,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation GETSET_PROPERTY(uint64_t, PeasantID, 0); GETSET_PROPERTY(winrt::guid, DesktopID, {}); GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {}); + GETSET_PROPERTY(uint64_t, Hwnd, 0); public: - WindowActivatedArgs(uint64_t peasantID, winrt::guid desktopID, winrt::Windows::Foundation::DateTime timestamp) : + WindowActivatedArgs(uint64_t peasantID, + uint64_t hwnd, + winrt::guid desktopID, + winrt::Windows::Foundation::DateTime timestamp) : _PeasantID{ peasantID }, + _Hwnd{ hwnd }, _DesktopID{ desktopID }, _ActivatedTime{ timestamp } {}; - static bool compare(const WindowActivatedArgs& lhs, const WindowActivatedArgs& rhs) { return lhs._ActivatedTime < rhs._ActivatedTime; } - static bool compare(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) { return lhs.ActivatedTime() < rhs.ActivatedTime(); } + + WindowActivatedArgs(uint64_t peasantID, + winrt::guid desktopID, + winrt::Windows::Foundation::DateTime timestamp) : + WindowActivatedArgs(peasantID, 0, desktopID, timestamp){}; }; } diff --git a/src/cascadia/Remoting/pch.h b/src/cascadia/Remoting/pch.h index 0fef7307ae5..f6d7a9de9ac 100644 --- a/src/cascadia/Remoting/pch.h +++ b/src/cascadia/Remoting/pch.h @@ -7,11 +7,17 @@ #pragma once +// Block minwindef.h min/max macros to prevent conflict +#define NOMINMAX + #define WIN32_LEAN_AND_MEAN #define NOMCX #define NOHELP #define NOCOMM +#include +#include + // Manually include til after we include Windows.Foundation to give it winrt superpowers #define BLOCK_TIL #include @@ -25,8 +31,6 @@ #include -#include - #include #include diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 121ecc0ee24..f918b3a25ae 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -556,23 +556,30 @@ winrt::fire_and_forget AppHost::_WindowActivated() if (auto peasant{ _windowManager.CurrentWindow() }) { - GUID currentDesktopGuid{ 0 }; - try - { - auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); - if (manager) - { - LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid)); - } - } - CATCH_LOG(); + const auto currentDesktopGuid{ _CurrentDesktopGuid() }; // TODO: projects/5 - in the future, we'll want to actually get the // desktop GUID in IslandWindow, and bubble that up here, then down to // the Peasant. For now, we're just leaving space for it. Remoting::WindowActivatedArgs args{ peasant.GetID(), + (uint64_t)_window->GetHandle(), currentDesktopGuid, winrt::clock().now() }; peasant.ActivateWindow(args); } } + +GUID AppHost::_CurrentDesktopGuid() +{ + GUID currentDesktopGuid{ 0 }; + try + { + auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + if (manager) + { + LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid)); + } + } + CATCH_LOG(); + return currentDesktopGuid; +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index db32ad67106..c8782d61e31 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -51,4 +51,5 @@ class AppHost void _FindTargetWindow(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + GUID _CurrentDesktopGuid(); }; From 9ab951c74a6a08a4caea7031f5ccb980b034fa7f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 3 Feb 2021 12:29:13 -0600 Subject: [PATCH 54/76] Start working on tests --- src/cascadia/Remoting/Monarch.cpp | 93 ++++++++----------- src/cascadia/Remoting/Monarch.h | 4 +- src/cascadia/TerminalApp/AppLogic.cpp | 33 +++---- .../UnitTests_Remoting/RemotingTests.cpp | 75 +++++++++++++++ src/cascadia/UnitTests_Remoting/pch.h | 11 +++ src/inc/WindowingBehavior.h | 10 ++ 6 files changed, 149 insertions(+), 77 deletions(-) create mode 100644 src/inc/WindowingBehavior.h diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 9c32ef5695c..aa3795c6c7f 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT license. #include "pch.h" +#include "WindowingBehavior.h" #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" @@ -21,6 +22,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation Monarch::Monarch() : _ourPID{ GetCurrentProcessId() } { + try + { + _desktopManager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + } + CATCH_LOG(); } // This is a private constructor to be used in unit tests, where we don't @@ -188,37 +194,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Monarch::_getMostRecentPeasantID(bool limitToCurrentDesktop) { - winrt::com_ptr manager{ nullptr }; - if (limitToCurrentDesktop) - { - try - { - manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); - } - CATCH_LOG(); - } - std::vector _mrus; for (auto& [g, vec] : _mruPeasants) { if (vec.size() > 0) { - if (limitToCurrentDesktop && manager) + if (limitToCurrentDesktop && _desktopManager) { - for (const auto& args : vec) + auto args{ *vec.begin() }; + BOOL onCurrentDesktop{ false }; + // SUCCEEDED_LOG will log if it failed, and + // return true if it SUCCEEDED + if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)args.Hwnd(), &onCurrentDesktop)) && + onCurrentDesktop) { - BOOL onCurrentDesktop{ false }; - // SUCCEEDED_LOG will log if it failed, and - // return true if it SUCCEEDED - if (SUCCEEDED_LOG(manager->IsWindowOnCurrentVirtualDesktop((HWND)args.Hwnd(), &onCurrentDesktop)) && - onCurrentDesktop) - { - _mrus.push_back(args); - std::push_heap(_mrus.begin(), - _mrus.end(), - Remoting::implementation::CompareWindowActivatedArgs()); - break; - } + _mrus.push_back(args); + std::push_heap(_mrus.begin(), + _mrus.end(), + Remoting::implementation::CompareWindowActivatedArgs()); } } else @@ -238,10 +231,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { return 0; } - // if (_mostRecentPeasant != 0) - // { - // return _mostRecentPeasant; - // } // We haven't yet been told the MRU peasant. Just use the first one. // This is just gonna be a random one, but really shouldn't happen @@ -291,42 +280,38 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // the parsed result. const auto targetWindow = findWindowArgs->ResultTargetWindow(); - // auto tmp = targetWindow; - // switch (targetWindow) - // { - // case -1: - // tmp = -1; - // break; - // case -2: // UseExistingSameDesktop - // tmp = _getMostRecentPeasantID(true); // TODO:MG for now, just use the MRU window. - // break; - // case -3: // UseExisting - // tmp = _getMostRecentPeasantID(false); // TODO:MG for now, just use the MRU window. - // break; - // } - - // If there's a valid ID returned, then let's try and find the peasant that goes with it. - if (targetWindow >= 0 || targetWindow == -2 || targetWindow == -3) + // If there's a valid ID returned, then let's try and find the peasant + // that goes with it. Alternatively, if we were given a magic windowing + // constant, we can use that to look up an appropriate peasant. + if (targetWindow >= 0 || + targetWindow == WindowingBehaviorUseExistingSameDesktop || + targetWindow == WindowingBehaviorUseExisting) { uint64_t windowID = 0; switch (targetWindow) { - case -2: // UseExistingSameDesktop - windowID = _getMostRecentPeasantID(true); // TODO:MG for now, just use the MRU window. + case WindowingBehaviorUseCurrent: + case WindowingBehaviorUseExistingSameDesktop: + // TODO:projects/5 for now, just use the MRU window. Technically, + // UseExistingSameDesktop and UseCurrent are different. + // UseCurrent implies that we should try to do the WT_SESSION + // lookup to find the window that spawned this process (then + // fall back to sameDesktop if we can't find a match). For now, + // it's good enough to just try to find a match on this desktop. + windowID = _getMostRecentPeasantID(true); break; - case 0: // UseExisting - case -3: // UseExisting - windowID = _getMostRecentPeasantID(false); // TODO:MG for now, just use the MRU window. + case WindowingBehaviorUseExisting: + windowID = _getMostRecentPeasantID(false); break; default: windowID = ::base::saturated_cast(targetWindow); break; } - // if (windowID == 0) - // { - // windowID = _getMostRecentPeasantID(false); - // } + // If_getMostRecentPeasantID returns 0 above, then we couldn't find + // a matching window for that style of windowing. _getPeasant will + // return nullptr, and we'll fall through to the "create a new + // window" branch below. if (auto targetPeasant{ _getPeasant(windowID) }) { @@ -377,6 +362,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } + // If we get here, we couldn't find an existing window. Make a new one. + TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_NewWindow", TraceLoggingInt64(targetWindow, "targetWindow", "The provided ID"), diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 5394039d0bd..e0eb9322fa1 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -57,8 +57,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t _nextPeasantID{ 1 }; uint64_t _thisPeasantID{ 0 }; - // uint64_t _mostRecentPeasant{ 0 }; - // winrt::Windows::Foundation::DateTime _lastActivatedTime{}; + + winrt::com_ptr _desktopManager{ nullptr }; std::unordered_map _peasants; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index eafc775140b..d29f0a2a88e 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "AppLogic.h" +#include "WindowingBehavior.h" #include "AppLogic.g.cpp" #include @@ -1194,9 +1195,11 @@ namespace winrt::TerminalApp::implementation // - args: an array of strings to process as a commandline. These args can contain spaces // Return Value: // - 0: We should handle the args "in the current window". - // - -1: We should handle the args in a new window - // - -2: We should handle the args "in the current window ON THIS DESKTOP" - // - -3: We should handle the args "in the current window ON ANY DESKTOP" + // - WindowingBehaviorUseNew: We should handle the args in a new window + // - WindowingBehaviorUseExistingSameDesktop: We should handle the args "in + // the current window ON THIS DESKTOP" + // - WindowingBehaviorUseExisting: We should handle the args "in the current + // window ON ANY DESKTOP" // - anything else: We should handle the commandline in the window with the given ID. int32_t AppLogic::FindTargetWindow(array_view args) { @@ -1221,29 +1224,15 @@ namespace winrt::TerminalApp::implementation switch (windowingBehavior) { case WindowingMode::UseNew: - return -1; + return WindowingBehaviorUseNew; case WindowingMode::UseExistingSameDesktop: - return -2; + return WindowingBehaviorUseExistingSameDesktop; case WindowingMode::UseExisting: - return -3; + return WindowingBehaviorUseExisting; default: - return -1; + return WindowingBehaviorUseNew; } } - - // return; - - // TODO:projects/5 - // - // In the future, we'll want to use the windowingBehavior setting to - // determine what happens when a window ID wasn't manually provided. - // - // Maybe that'd be a special return value out of here, to tell the - // monarch to do something special: - // - // -1 -> create a new window - // -2 -> find the mru, this desktop - // -3 -> MRU, any desktop (is this not just 0?) } // Any unsuccessful parse will be a new window. That new window will try @@ -1257,7 +1246,7 @@ namespace winrt::TerminalApp::implementation // create a new window. Then, in that new window, we'll try to set the // StartupActions, which will again fail, returning the correct error // message. - return -1; + return WindowingBehaviorUseNew; } // Method Description: diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index f9c55750047..b4e816e5ac7 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -79,6 +79,8 @@ namespace RemotingUnitTests TEST_METHOD(ProposeCommandlineNonExistentWindow); TEST_METHOD(ProposeCommandlineDeadWindow); + TEST_METHOD(MostRecentWindowSameDesktops); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -602,4 +604,77 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(1u, result.Id().Value()); } } + + // OH GOD We have to create our own IVirtualDesktopManager impl that can be + // subbed in for testing. I hate it. We can at least start by just calling + // HandleActivatePeasant and ensuring that the map of vectors is sensible + + // Tests: + // * Make windows on different desktops. Ensure that _mruPeasants has the + // right number of entries, each with the right number of peasants, in the + // right order + // * Make windows on different desktops. Move one of the peasants to a + // different desktop. Ensure that _mruPeasants has the right number of + // entries, each with the right number of peasants, in the right order + // * Test that ProposeCommandline combined with FindTargetWindowArgs that + // returns WindowingBehaviorUseExisting proposes the commandline to the + // most recent of all windows. + // - change the most recent window, and it arrives in the new one + + void RemotingTests::MostRecentWindowSameDesktops() + { + Log::Comment(L"Test proposing a commandline for a peasant that previously died"); + + // const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}") }; + const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + // p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& /*cmdlineArgs*/) { + // Log::Comment(L"Commandline dispatched to p1"); + // VERIFY_IS_TRUE(false, L"This should not happen, this peasant should be dead."); + // }); + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + // p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { + // Log::Comment(L"Commandline dispatched to p2"); + // VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); + // VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); + // }); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + guid1, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + } } diff --git a/src/cascadia/UnitTests_Remoting/pch.h b/src/cascadia/UnitTests_Remoting/pch.h index da79b2d4fd8..8239e461c15 100644 --- a/src/cascadia/UnitTests_Remoting/pch.h +++ b/src/cascadia/UnitTests_Remoting/pch.h @@ -5,6 +5,17 @@ Licensed under the MIT license. #pragma once +// Block minwindef.h min/max macros to prevent conflict +#define NOMINMAX + +#define WIN32_LEAN_AND_MEAN +#define NOMCX +#define NOHELP +#define NOCOMM + +#include +#include + // Manually include til after we include Windows.Foundation to give it winrt superpowers #define BLOCK_TIL // This includes support libraries from the CRT, STL, WIL, and GSL diff --git a/src/inc/WindowingBehavior.h b/src/inc/WindowingBehavior.h new file mode 100644 index 00000000000..1a93238ece7 --- /dev/null +++ b/src/inc/WindowingBehavior.h @@ -0,0 +1,10 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ + +#pragma once +constexpr int64_t WindowingBehaviorUseCurrent{ 0 }; +constexpr int64_t WindowingBehaviorUseNew{ -1 }; +constexpr int64_t WindowingBehaviorUseExistingSameDesktop{ -2 }; +constexpr int64_t WindowingBehaviorUseExisting{ -3 }; From 6dc0feb8a9741f1cc16709ded29a6e70c86bbe9e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 2 Feb 2021 15:54:36 -0600 Subject: [PATCH 55/76] What? No yea I always run the tests (cherry picked from commit 2367db1d832ee5307bb62386ebd0f9510be9c166) --- .../UnitTests_Remoting/RemotingTests.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index b4e816e5ac7..558eba98814 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -114,7 +114,7 @@ namespace RemotingUnitTests void RemotingTests::_findTargetWindowHelper(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args) { - const auto arguments = args.Args().Args(); + const auto arguments = args.Args().Commandline(); if (arguments.size() > 0) { const auto index = std::stoi(arguments.at(0).c_str()); @@ -381,8 +381,8 @@ namespace RemotingUnitTests p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { Log::Comment(L"Commandline dispatched to p1"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); - VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Args().at(1)); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); + VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1)); }); std::vector args{ L"1", L"arg[1]" }; @@ -445,8 +445,8 @@ namespace RemotingUnitTests m0->AddPeasant(*p1); p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { Log::Comment(L"Commandline dispatched to p1"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); - VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Args().at(1)); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); + VERIFY_ARE_EQUAL(L"arg[1]", cmdlineArgs.Commandline().at(1)); }); std::vector p1Args{ L"0", L"arg[1]" }; @@ -476,8 +476,8 @@ namespace RemotingUnitTests m0->AddPeasant(*p2); p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { Log::Comment(L"Commandline dispatched to p2"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); - VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); + VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1)); }); { @@ -574,8 +574,8 @@ namespace RemotingUnitTests m0->AddPeasant(*p2); p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { Log::Comment(L"Commandline dispatched to p2"); - VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); - VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); + VERIFY_IS_GREATER_THAN(cmdlineArgs.Commandline().size(), 1u); + VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Commandline().at(1)); }); std::vector p1Args{ L"1", L"arg[1]" }; From 9e94b36961ea90344389f0772baea8f48df0da47 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 3 Feb 2021 13:00:43 -0600 Subject: [PATCH 56/76] I guess the tests weren't too bad --- src/cascadia/Remoting/Monarch.cpp | 14 +- .../UnitTests_Remoting/RemotingTests.cpp | 315 ++++++++++++++++-- 2 files changed, 292 insertions(+), 37 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index aa3795c6c7f..ece7c58489a 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -160,8 +160,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (result != std::end(vec)) { vec.erase(result); + std::make_heap(vec.begin(), vec.end(), Remoting::implementation::CompareWindowActivatedArgs()); + continue; } - std::make_heap(vec.begin(), vec.end(), Remoting::implementation::CompareWindowActivatedArgs()); } // * If the current desktop doesn't have a vector, add one. if (_mruPeasants.find(args.DesktopID()) == _mruPeasants.end()) @@ -174,20 +175,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mruPeasants[args.DesktopID()].end(), Remoting::implementation::CompareWindowActivatedArgs()); - // // For now, we'll just pay attention to whoever the most recent peasant - // // was. We're not too worried about the mru peasant dying. Worst case - - // // when the user executes a `wt -w 0`, we won't be able to find that - // // peasant, and it'll open in a new window instead of the current one. - // if (args.ActivatedTime() > _lastActivatedTime) - // { - // _mostRecentPeasant = args.PeasantID(); - // _lastActivatedTime = args.ActivatedTime(); - // } - TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"), // TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"), + // TODO:MG add the GUID here, better logging TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided args.ActivatedTime()"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 558eba98814..46563f29a8e 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -80,6 +80,9 @@ namespace RemotingUnitTests TEST_METHOD(ProposeCommandlineDeadWindow); TEST_METHOD(MostRecentWindowSameDesktops); + TEST_METHOD(MostRecentWindowDifferentDesktops); + TEST_METHOD(MostRecentWindowMoveDesktops); + TEST_METHOD(GetMostRecentAnyDesktop); TEST_CLASS_SETUP(ClassSetup) { @@ -605,27 +608,18 @@ namespace RemotingUnitTests } } - // OH GOD We have to create our own IVirtualDesktopManager impl that can be - // subbed in for testing. I hate it. We can at least start by just calling - // HandleActivatePeasant and ensuring that the map of vectors is sensible - - // Tests: - // * Make windows on different desktops. Ensure that _mruPeasants has the - // right number of entries, each with the right number of peasants, in the - // right order - // * Make windows on different desktops. Move one of the peasants to a - // different desktop. Ensure that _mruPeasants has the right number of - // entries, each with the right number of peasants, in the right order - // * Test that ProposeCommandline combined with FindTargetWindowArgs that - // returns WindowingBehaviorUseExisting proposes the commandline to the - // most recent of all windows. - // - change the most recent window, and it arrives in the new one + // TODO:projects/5 + // + // In order to test WindowingBehaviorUseExistingSameDesktop, we'll have to + // create our own IVirtualDesktopManager implementation that can be subbed + // in for testing. We can't _actually_ create HWNDs as a part of the test + // and move them to different desktops. Instead, we'll have to create a stub + // impl that can fake a result for IsWindowOnCurrentVirtualDesktop. void RemotingTests::MostRecentWindowSameDesktops() { - Log::Comment(L"Test proposing a commandline for a peasant that previously died"); + Log::Comment(L"Make windows on the same desktop. Validate the contents of _mruPeasants are as expected."); - // const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{00000000-0000-0000-0000-000000000000}") }; const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; @@ -641,10 +635,6 @@ namespace RemotingUnitTests p1.attach(new Remoting::implementation::Peasant(peasant1PID)); VERIFY_IS_NOT_NULL(p1); m0->AddPeasant(*p1); - // p1->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& /*cmdlineArgs*/) { - // Log::Comment(L"Commandline dispatched to p1"); - // VERIFY_IS_TRUE(false, L"This should not happen, this peasant should be dead."); - // }); Log::Comment(L"Add a second peasant"); const auto peasant2PID = 34567u; @@ -652,11 +642,6 @@ namespace RemotingUnitTests p2.attach(new Remoting::implementation::Peasant(peasant2PID)); VERIFY_IS_NOT_NULL(p2); m0->AddPeasant(*p2); - // p2->ExecuteCommandlineRequested([&](auto&&, const Remoting::CommandlineArgs& cmdlineArgs) { - // Log::Comment(L"Commandline dispatched to p2"); - // VERIFY_IS_GREATER_THAN(cmdlineArgs.Args().size(), 1u); - // VERIFY_ARE_EQUAL(L"this is for p2", cmdlineArgs.Args().at(1)); - // }); { Log::Comment(L"Activate the first peasant, first desktop"); @@ -676,5 +661,283 @@ namespace RemotingUnitTests VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + } + + void RemotingTests::MostRecentWindowDifferentDesktops() + { + Log::Comment(L"Make windows on different desktops. Validate the contents of _mruPeasants are as expected."); + + const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + guid2, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + + Log::Comment(L"Add a third peasant"); + const auto peasant3PID = 45678u; + com_ptr p3; + p3.attach(new Remoting::implementation::Peasant(peasant3PID)); + VERIFY_IS_NOT_NULL(p3); + m0->AddPeasant(*p3); + { + Log::Comment(L"Activate the third peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + guid1, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + } + + void RemotingTests::MostRecentWindowMoveDesktops() + { + Log::Comment(L"Make windows on different desktops. Move one to another desktop. Validate the contents of _mruPeasants are as expected."); + + const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + guid2, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + + Log::Comment(L"Add a third peasant"); + const auto peasant3PID = 45678u; + com_ptr p3; + p3.attach(new Remoting::implementation::Peasant(peasant3PID)); + VERIFY_IS_NOT_NULL(p3); + m0->AddPeasant(*p3); + { + Log::Comment(L"Activate the third peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + guid1, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + + { + Log::Comment(L"Activate the first peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid2, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][1].PeasantID()); + + { + Log::Comment(L"Activate the third peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + guid2, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(0u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(3u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + + // Because the vector is internally a heap, we actually can't be sure + // what the ordering of the subsequent elements will be. We can check + // the order consistently for 2 elements. For three+, all but the first + // element will be in an indeterminate order. + // + // VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid2][1].PeasantID()); + // VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][2].PeasantID()); + + { + Log::Comment(L"Activate the second peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + guid1, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid2].size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid2][1].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + } + + void RemotingTests::GetMostRecentAnyDesktop() + { + Log::Comment(L"Make windows on different desktops. Confirm that getting the most recent of all windows works as expected."); + + const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + guid2, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(p2->GetID(), m0->_getMostRecentPeasantID(false)); + + Log::Comment(L"Add a third peasant"); + const auto peasant3PID = 45678u; + com_ptr p3; + p3.attach(new Remoting::implementation::Peasant(peasant3PID)); + VERIFY_IS_NOT_NULL(p3); + m0->AddPeasant(*p3); + { + Log::Comment(L"Activate the third peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p3->GetID(), + guid1, + winrt::clock().now() }; + p3->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(p3->GetID(), m0->_getMostRecentPeasantID(false)); + + { + Log::Comment(L"Activate the first peasant, second desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid2, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false)); } } From 031139b42e959f85dce79dbb996e73852aebc502 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 9 Feb 2021 15:38:34 -0600 Subject: [PATCH 57/76] !!! This needs to go to the parent branch !!! Turns out, I did _NOT_ do my diligence try/catching this as needed. This is all going to be really bad. (cherry picked from commit 26815e87f37bba68915d138fd4c61efd78df61db) --- src/cascadia/Remoting/Monarch.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ece7c58489a..1720661d97d 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -156,7 +156,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto result = std::find_if(vec.begin(), vec.end(), - [args](auto other) { return args.PeasantID() == other.PeasantID(); }); + [args](auto other) { + try + { + return args.PeasantID() == other.PeasantID(); + } + CATCH_LOG(); // TODO:MG Oh no, this needs to be fixed in windowingBehavior + return false; + }); if (result != std::end(vec)) { vec.erase(result); From 683c146452954055e849fc212bd81ff69b09a682 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 10 Feb 2021 12:25:38 -0600 Subject: [PATCH 58/76] Fix a weird bug in windowingBehavior:useExisting Repro steps were: * use windowingBehavior:useExisting * Create two windows * close the second one, but make sure that focus falls onto another app (not the first wt window) * switch to a second desktop * run `wt`. That _should_ still create a tab in the first window, on the other desktop. The issue was though,t hat we'd agree that window 2 was the MRU one, and try to get it to execute the commandline. That would fail, and we'd fall back to "make a new window". --- src/cascadia/Remoting/Monarch.cpp | 217 ++++++++++++++++++++------ src/cascadia/Remoting/Monarch.h | 6 + src/cascadia/Remoting/WindowManager.h | 1 + 3 files changed, 174 insertions(+), 50 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index dd93d5aac72..ddac11b481d 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -6,7 +6,6 @@ #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" -#include "WindowActivatedArgs.h" #include "ProposeCommandlineResult.h" #include "Monarch.g.cpp" @@ -137,106 +136,215 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation LOG_CAUGHT_EXCEPTION(); // Remove the peasant from the list of peasants _peasants.erase(peasantID); + + // TODO: Remove the peasant from the MRU windows mapping. They're dead. They can't be the MRU anymore. + _clearOldMruEntries(peasantID); return nullptr; } } void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args) { - // TODO:projects/5 Use a heap/priority queue per-desktop to track which - // peasant was the most recent per-desktop. When we want to get the most - // recent of all desktops (WindowingBehavior::UseExisting), then use the - // most recent of all desktops. - // const auto oldLastActiveTime = _lastActivatedTime.time_since_epoch().count(); - const auto newLastActiveTime = args.ActivatedTime().time_since_epoch().count(); + // Start by making a local copy of these args. It's easier for us if our + // tracking of these args is all in-proc. That way, the only thing that + // could fail due to the peasant dying is _this first copy_. + winrt::com_ptr localArgs{ nullptr }; + try + { + localArgs = winrt::make_self(args.PeasantID(), + args.Hwnd(), + args.DesktopID(), + args.ActivatedTime()); + _doHandleActivatePeasant(localArgs); + } + catch (...) + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_HandleActivatePeasant_Failed", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + } + } + void Monarch::_clearOldMruEntries(const uint64_t peasantID) + { // * Check all the current lists to look for this peasant. // remove it from any where it exists. for (auto& [g, vec] : _mruPeasants) { auto result = std::find_if(vec.begin(), vec.end(), - [args](auto other) { - try - { - return args.PeasantID() == other.PeasantID(); - } - CATCH_LOG(); // TODO:MG Oh no, this needs to be fixed in windowingBehavior - return false; + [peasantID](auto other) { + return peasantID == other.PeasantID(); }); if (result != std::end(vec)) { vec.erase(result); - std::make_heap(vec.begin(), vec.end(), Remoting::implementation::CompareWindowActivatedArgs()); + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_RemovedPeasantFromDesktop", + TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"), + TraceLoggingGuid(g, "desktopGuid", "The GUID of the previous desktop the window was on"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + std::make_heap(vec.begin(), + vec.end(), + Remoting::implementation::CompareWindowActivatedArgs()); continue; } } + } + + void Monarch::_doHandleActivatePeasant(const winrt::com_ptr& localArgs) + { + const auto newLastActiveTime = localArgs->ActivatedTime().time_since_epoch().count(); + + // * Check all the current lists to look for this peasant. + // remove it from any where it exists. + _clearOldMruEntries(localArgs->PeasantID()); + // * If the current desktop doesn't have a vector, add one. - if (_mruPeasants.find(args.DesktopID()) == _mruPeasants.end()) + const auto desktopGuid{ localArgs->DesktopID() }; + if (_mruPeasants.find(desktopGuid) == _mruPeasants.end()) { - _mruPeasants[args.DesktopID()] = std::vector(); + _mruPeasants[desktopGuid] = std::vector(); } // * Add this args to the queue for the given desktop. - _mruPeasants[args.DesktopID()].push_back(args); - std::push_heap(_mruPeasants[args.DesktopID()].begin(), - _mruPeasants[args.DesktopID()].end(), + _mruPeasants[desktopGuid].push_back(*localArgs); + std::push_heap(_mruPeasants[desktopGuid].begin(), + _mruPeasants[desktopGuid].end(), Remoting::implementation::CompareWindowActivatedArgs()); TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", - TraceLoggingUInt64(args.PeasantID(), "peasantID", "the ID of the activated peasant"), + TraceLoggingUInt64(localArgs->PeasantID(), "peasantID", "the ID of the activated peasant"), // TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"), - // TODO:MG add the GUID here, better logging - TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided args.ActivatedTime()"), + TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), + TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided localArgs->ActivatedTime()"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - uint64_t Monarch::_getMostRecentPeasantID(bool limitToCurrentDesktop) + void Monarch::_collectMRUPeasant(const bool limitToCurrentDesktop, + std::vector& mrus, + std::vector& windowsForDesktop) { - std::vector _mrus; - for (auto& [g, vec] : _mruPeasants) + while (!windowsForDesktop.empty()) { - if (vec.size() > 0) + const auto mruWindowArgs{ *windowsForDesktop.begin() }; + const auto desktopGuid = mruWindowArgs.DesktopID(); + // Trying to get the peasant will verify the peasant is still alive. + // If they aren't, the peasant will be removed from the ID->Peasant + // map, and from the the desktop->[Peasant] map as well. + // * If the peasant is dead, just try this loop again. + // * Otherwise, try and collect it. + auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; + if (!peasant) { - if (limitToCurrentDesktop && _desktopManager) - { - auto args{ *vec.begin() }; - BOOL onCurrentDesktop{ false }; - // SUCCEEDED_LOG will log if it failed, and - // return true if it SUCCEEDED - if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)args.Hwnd(), &onCurrentDesktop)) && - onCurrentDesktop) - { - _mrus.push_back(args); - std::push_heap(_mrus.begin(), - _mrus.end(), - Remoting::implementation::CompareWindowActivatedArgs()); - } - } - else + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_Collect_WasDead", + TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "We thought this peasant was the MRU one, but it was actually already dead."), + TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + continue; + } + + // Here, the peasant mruWindowArgs belongs to is still alive. + if (limitToCurrentDesktop && _desktopManager) + { + // Check if this peasant is actually on this desktop. We can't + // simply get the GUID of the current desktop. We have to ask if + // the HWND is on the current desktop. + BOOL onCurrentDesktop{ false }; + + // SUCCEEDED_LOG will log if it failed, and return true if it + // SUCCEEDED + if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), &onCurrentDesktop)) && + onCurrentDesktop) { - _mrus.push_back(*vec.begin()); - std::push_heap(_mrus.begin(), - _mrus.end(), + mrus.push_back(mruWindowArgs); + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_Collect", + TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "the ID of the MRU peasant for a desktop"), + TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), + TraceLoggingBoolean(limitToCurrentDesktop, "limitToCurrentDesktop", "TODO"), + TraceLoggingBool(onCurrentDesktop, "limitToCurrentDesktop", "TODO"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // Update the mrus heap, so the actually most recent args is + // at the front. + std::push_heap(mrus.begin(), + mrus.end(), Remoting::implementation::CompareWindowActivatedArgs()); } + // If the MRU window for the list of windows on this desktop + // _isn't_ on the current desktop, then _none_ of the windows on + // this desktop will be. We'll break out of the loop below. + } + else + { + // Here, we don't care about which desktop the window is on. We'll add this window to the list of MRU windows. + mrus.push_back(mruWindowArgs); + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_Collect", + TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "the ID of the MRU peasant for a desktop"), + TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), + TraceLoggingBoolean(limitToCurrentDesktop, "limitToCurrentDesktop", "TODO"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // Update the mrus heap, so the actually most recent args is at + // the front. + std::push_heap(mrus.begin(), + mrus.end(), + Remoting::implementation::CompareWindowActivatedArgs()); + } + + // Exit the loop. Either we took the most recent window from the + // windows on the given desktop, or we decided not to. Either way, + // we don't need to keep looping. + break; + } + } + + uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop) + { + std::vector mrus; + for (auto& [g, vec] : _mruPeasants) + { + if (vec.empty()) + { + continue; } + _collectMRUPeasant(limitToCurrentDesktop, mrus, vec); } - if (!_mrus.empty()) + + if (!mrus.empty()) { - return _mrus.begin()->PeasantID(); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_getMostRecentPeasantID_Found", + TraceLoggingUInt64(mrus.begin()->PeasantID(), "peasantID", "The ID of the MRU peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + return mrus.begin()->PeasantID(); } else if (limitToCurrentDesktop) { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_getMostRecentPeasantID_NotFound_ThisDesktop", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); return 0; } + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_getMostRecentPeasantID_NotFound", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // We haven't yet been told the MRU peasant. Just use the first one. // This is just gonna be a random one, but really shouldn't happen // in practice. The WindowManager should set the MRU peasant // immediately as soon as it creates the monarch/peasant for the // first window. - else if (_peasants.size() > 0) + if (_peasants.size() > 0) { try { @@ -279,6 +387,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // the parsed result. const auto targetWindow = findWindowArgs->ResultTargetWindow(); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline", + TraceLoggingInt64(targetWindow, "targetWindow", "The window ID the args specified"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // If there's a valid ID returned, then let's try and find the peasant // that goes with it. Alternatively, if we were given a magic windowing // constant, we can use that to look up an appropriate peasant. @@ -307,6 +420,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation break; } + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_ProposeCommandline", + TraceLoggingInt64(windowID, "windowID", "The actual peasant ID we evaluated the window ID as"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // If_getMostRecentPeasantID returns 0 above, then we couldn't find // a matching window for that style of windowing. _getPeasant will // return nullptr, and we'll fall through to the "create a new @@ -370,5 +488,4 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // In this case, no usable ID was provided. Return { true, nullopt } return winrt::make(true); } - } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index e0eb9322fa1..0606ab59235 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -6,6 +6,7 @@ #include "Monarch.g.h" #include "Peasant.h" #include "../cascadia/inc/cppwinrt_utils.h" +#include "WindowActivatedArgs.h" // We sure different GUIDs here depending on whether we're running a Release, // Preview, or Dev build. This ensures that different installs don't @@ -69,6 +70,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); + void _doHandleActivatePeasant(const winrt::com_ptr& args); + void _collectMRUPeasant(const bool limitToCurrentDesktop, + std::vector& mrus, + std::vector& windowsForDesktop); + void _clearOldMruEntries(const uint64_t peasantID); friend class RemotingUnitTests::RemotingTests; }; diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index bc257ef06b6..a2db645b7b4 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -59,6 +59,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _performElection(); void _createPeasantThread(); void _waitOnMonarchThread(); + void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; From 8072efa7bd12c187260871cf7d6c9be3f39077f1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 10 Feb 2021 12:56:19 -0600 Subject: [PATCH 59/76] Add a test for this weird case from the last commit --- src/cascadia/Remoting/Monarch.cpp | 51 +++++++++++----- src/cascadia/Remoting/WindowManager.cpp | 35 ++++++++++- .../UnitTests_Remoting/RemotingTests.cpp | 60 +++++++++++++++++++ 3 files changed, 129 insertions(+), 17 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index ddac11b481d..bdc572139db 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -45,7 +45,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } // Method Description: - // - Add the given peasant to the list of peasants we're tracking. This Peasant may have already been assigned an ID. If it hasn't, then give it an ID. + // - Add the given peasant to the list of peasants we're tracking. This + // Peasant may have already been assigned an ID. If it hasn't, then give + // it an ID. // Arguments: // - peasant: the new Peasant to track. // Return Value: @@ -137,7 +139,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Remove the peasant from the list of peasants _peasants.erase(peasantID); - // TODO: Remove the peasant from the MRU windows mapping. They're dead. They can't be the MRU anymore. + // Remove the peasant from the MRU windows mapping. They're dead. + // They can't be the MRU anymore. _clearOldMruEntries(peasantID); return nullptr; } @@ -217,7 +220,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", TraceLoggingUInt64(localArgs->PeasantID(), "peasantID", "the ID of the activated peasant"), - // TraceLoggingInt64(oldLastActiveTime, "oldLastActiveTime", "The previous lastActiveTime"), TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), TraceLoggingInt64(newLastActiveTime, "newLastActiveTime", "The provided localArgs->ActivatedTime()"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); @@ -241,7 +243,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { TraceLoggingWrite(g_hRemotingProvider, "Monarch_Collect_WasDead", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "We thought this peasant was the MRU one, but it was actually already dead."), + TraceLoggingUInt64(mruWindowArgs.PeasantID(), + "peasantID", + "We thought this peasant was the MRU one, but it was actually already dead."), TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); continue; @@ -257,17 +261,26 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // SUCCEEDED_LOG will log if it failed, and return true if it // SUCCEEDED - if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), &onCurrentDesktop)) && + if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), + &onCurrentDesktop)) && onCurrentDesktop) { mrus.push_back(mruWindowArgs); TraceLoggingWrite(g_hRemotingProvider, "Monarch_Collect", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "the ID of the MRU peasant for a desktop"), - TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), - TraceLoggingBoolean(limitToCurrentDesktop, "limitToCurrentDesktop", "TODO"), - TraceLoggingBool(onCurrentDesktop, "limitToCurrentDesktop", "TODO"), + TraceLoggingUInt64(mruWindowArgs.PeasantID(), + "peasantID", + "the ID of the MRU peasant for a desktop"), + TraceLoggingGuid(desktopGuid, + "desktopGuid", + "The GUID of the desktop the window is on"), + TraceLoggingBoolean(limitToCurrentDesktop, + "limitToCurrentDesktop", + "True if we should only search for a window on the current desktop"), + TraceLoggingBool(onCurrentDesktop, + "onCurrentDesktop", + "true if this window was in fact on the current desktop"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // Update the mrus heap, so the actually most recent args is @@ -287,7 +300,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Monarch_Collect", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "the ID of the MRU peasant for a desktop"), + TraceLoggingUInt64(mruWindowArgs.PeasantID(), + "peasantID", + "the ID of the MRU peasant for a desktop"), TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), TraceLoggingBoolean(limitToCurrentDesktop, "limitToCurrentDesktop", "TODO"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); @@ -422,7 +437,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline", - TraceLoggingInt64(windowID, "windowID", "The actual peasant ID we evaluated the window ID as"), + TraceLoggingInt64(windowID, + "windowID", + "The actual peasant ID we evaluated the window ID as"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); // If_getMostRecentPeasantID returns 0 above, then we couldn't find @@ -455,9 +472,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", - TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"), + TraceLoggingUInt64(windowID, + "peasantID", + "the ID of the peasant the commandline waws intended for"), TraceLoggingBoolean(true, "foundMatch", "true if we found a peasant with that ID"), - TraceLoggingBoolean(!result->ShouldCreateWindow(), "succeeded", "true if we successfully dispatched the commandline to the peasant"), + TraceLoggingBoolean(!result->ShouldCreateWindow(), + "succeeded", + "true if we successfully dispatched the commandline to the peasant"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); return *result; } @@ -469,7 +490,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingWrite(g_hRemotingProvider, "Monarch_ProposeCommandline_Existing", - TraceLoggingUInt64(windowID, "peasantID", "the ID of the peasant the commandline waws intended for"), + TraceLoggingUInt64(windowID, + "peasantID", + "the ID of the peasant the commandline waws intended for"), TraceLoggingBoolean(false, "foundMatch", "true if we found a peasant with that ID"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 01a422a690d..15b0f8773ed 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -5,6 +5,8 @@ #include "WindowManager.h" #include "MonarchFactory.h" #include "CommandlineArgs.h" +#include "WindowingBehavior.h" +#include "FindTargetWindowArgs.h" #include "WindowManager.g.cpp" #include "../../types/inc/utils.hpp" @@ -69,6 +71,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Otherwise, the King will tell us if we should make a new window _shouldCreateWindow = _isKing; std::optional givenID; + winrt::hstring givenName = L""; if (!_isKing) { // The monarch may respond back "you should be a new @@ -86,7 +89,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { givenID = result.Id().Value(); } - + givenName = result.WindowName(); // TraceLogging doesn't have a good solution for logging an // optional. So we have to repeat the calls here: if (givenID) @@ -95,6 +98,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "WindowManager_ProposeCommandline", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), + TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } else @@ -103,6 +107,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "WindowManager_ProposeCommandline", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), TraceLoggingPointer(nullptr, "Id", "No ID provided"), + TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } } @@ -110,9 +115,29 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // We're the monarch, we don't need to propose anything. We're just // going to do it. + // + // However, we _do_ need to ask what our name should be. It's + // possible someone started the _first_ wt with something like `wt + // -w king` as the commandline - we want to make sure we set our + // name to "king". + auto findWindowArgs{ winrt::make_self(args) }; + _raiseFindTargetWindowRequested(this, *findWindowArgs); + + auto responseId = args.ResultTargetWindow(); + if (responseId > 0) + { + givenID = ::base::saturated_cast(responseId); + } + else if (responseId == WindowingBehaviorUseName) + { + givenName = args.ResultTargetWindowName(); + } + TraceLoggingWrite(g_hRemotingProvider, "WindowManager_ProposeCommandline_AsMonarch", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), + TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } @@ -120,7 +145,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // If we should create a new window, then instantiate our Peasant // instance, and tell that peasant to handle that commandline. - _createOurPeasant({ givenID }); + _createOurPeasant({ givenID }, givenName); // Spawn a thread to wait on the monarch, and handle the election if (!_isKing) @@ -208,13 +233,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return (ourPID == kingPID); } - Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID) + Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID, + const winrt::hstring& givenName) { auto p = winrt::make_self(); if (givenID) { p->AssignID(givenID.value()); } + + // If the name wasn't specified, this will be an empty string. + p->WindowName(givenName); _peasant = *p; // Try to add us to the monarch. If that fails, try to find a monarch diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 46563f29a8e..891fdb42932 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -83,6 +83,7 @@ namespace RemotingUnitTests TEST_METHOD(MostRecentWindowDifferentDesktops); TEST_METHOD(MostRecentWindowMoveDesktops); TEST_METHOD(GetMostRecentAnyDesktop); + TEST_METHOD(MostRecentIsDead); TEST_CLASS_SETUP(ClassSetup) { @@ -940,4 +941,63 @@ namespace RemotingUnitTests } VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false)); } + + void RemotingTests::MostRecentIsDead() + { + Log::Comment(L"Make two windows. Activate the first, then the second. Kill the second. The most recent should be the _first_ window."); + + const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + const auto monarch0PID = 12345u; + com_ptr m0; + m0.attach(new Remoting::implementation::Monarch(monarch0PID)); + VERIFY_IS_NOT_NULL(m0); + m0->FindTargetWindowRequested(&RemotingTests::_findTargetWindowHelper); + + Log::Comment(L"Add a peasant"); + const auto peasant1PID = 23456u; + com_ptr p1; + p1.attach(new Remoting::implementation::Peasant(peasant1PID)); + VERIFY_IS_NOT_NULL(p1); + m0->AddPeasant(*p1); + + Log::Comment(L"Add a second peasant"); + const auto peasant2PID = 34567u; + com_ptr p2; + p2.attach(new Remoting::implementation::Peasant(peasant2PID)); + VERIFY_IS_NOT_NULL(p2); + m0->AddPeasant(*p2); + + { + Log::Comment(L"Activate the first peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p1->GetID(), + guid1, + winrt::clock().now() }; + p1->ActivateWindow(activatedArgs); + } + { + Log::Comment(L"Activate the second peasant, first desktop"); + Remoting::WindowActivatedArgs activatedArgs{ p2->GetID(), + guid1, + winrt::clock().now() }; + p2->ActivateWindow(activatedArgs); + } + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + + Log::Comment(L"Kill peasant 2"); + RemotingTests::_killPeasant(m0, p2->GetID()); + Log::Comment(L"Peasant 1 should be the new MRU peasant"); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_getMostRecentPeasantID(false)); + + Log::Comment(L"Peasant 2 should not be in the monarch at all anymore"); + VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + } + } From 5edfe28f0707256db048e5684639f2f9d1c602fd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 10 Feb 2021 14:16:49 -0600 Subject: [PATCH 60/76] revert this file This got polluted here from another branch --- src/cascadia/Remoting/WindowManager.cpp | 35 +++---------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 15b0f8773ed..01a422a690d 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -5,8 +5,6 @@ #include "WindowManager.h" #include "MonarchFactory.h" #include "CommandlineArgs.h" -#include "WindowingBehavior.h" -#include "FindTargetWindowArgs.h" #include "WindowManager.g.cpp" #include "../../types/inc/utils.hpp" @@ -71,7 +69,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Otherwise, the King will tell us if we should make a new window _shouldCreateWindow = _isKing; std::optional givenID; - winrt::hstring givenName = L""; if (!_isKing) { // The monarch may respond back "you should be a new @@ -89,7 +86,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { givenID = result.Id().Value(); } - givenName = result.WindowName(); + // TraceLogging doesn't have a good solution for logging an // optional. So we have to repeat the calls here: if (givenID) @@ -98,7 +95,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "WindowManager_ProposeCommandline", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } else @@ -107,7 +103,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "WindowManager_ProposeCommandline", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), TraceLoggingPointer(nullptr, "Id", "No ID provided"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } } @@ -115,29 +110,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // We're the monarch, we don't need to propose anything. We're just // going to do it. - // - // However, we _do_ need to ask what our name should be. It's - // possible someone started the _first_ wt with something like `wt - // -w king` as the commandline - we want to make sure we set our - // name to "king". - auto findWindowArgs{ winrt::make_self(args) }; - _raiseFindTargetWindowRequested(this, *findWindowArgs); - - auto responseId = args.ResultTargetWindow(); - if (responseId > 0) - { - givenID = ::base::saturated_cast(responseId); - } - else if (responseId == WindowingBehaviorUseName) - { - givenName = args.ResultTargetWindowName(); - } - TraceLoggingWrite(g_hRemotingProvider, "WindowManager_ProposeCommandline_AsMonarch", TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } @@ -145,7 +120,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { // If we should create a new window, then instantiate our Peasant // instance, and tell that peasant to handle that commandline. - _createOurPeasant({ givenID }, givenName); + _createOurPeasant({ givenID }); // Spawn a thread to wait on the monarch, and handle the election if (!_isKing) @@ -233,17 +208,13 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return (ourPID == kingPID); } - Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID, - const winrt::hstring& givenName) + Remoting::IPeasant WindowManager::_createOurPeasant(std::optional givenID) { auto p = winrt::make_self(); if (givenID) { p->AssignID(givenID.value()); } - - // If the name wasn't specified, this will be an empty string. - p->WindowName(givenName); _peasant = *p; // Try to add us to the monarch. If that fails, try to find a monarch From ccf8d6375aa02ce3ff45b9fbfec0c38592a44492 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 10 Feb 2021 14:17:02 -0600 Subject: [PATCH 61/76] Some spellcheck fixes --- .github/actions/spelling/dictionary/apis.txt | 1 + src/cascadia/Remoting/Monarch.cpp | 37 +++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.github/actions/spelling/dictionary/apis.txt b/.github/actions/spelling/dictionary/apis.txt index b1bbc345616..e06983408b1 100644 --- a/.github/actions/spelling/dictionary/apis.txt +++ b/.github/actions/spelling/dictionary/apis.txt @@ -40,6 +40,7 @@ IPeasant IStorage ITab ITaskbar +IVirtual LCID llabs llu diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index bdc572139db..dd5e66d9e41 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -226,7 +226,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } void Monarch::_collectMRUPeasant(const bool limitToCurrentDesktop, - std::vector& mrus, + std::vector& mruWindows, std::vector& windowsForDesktop) { while (!windowsForDesktop.empty()) @@ -265,7 +265,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation &onCurrentDesktop)) && onCurrentDesktop) { - mrus.push_back(mruWindowArgs); + mruWindows.push_back(mruWindowArgs); TraceLoggingWrite(g_hRemotingProvider, "Monarch_Collect", @@ -283,10 +283,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "true if this window was in fact on the current desktop"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // Update the mrus heap, so the actually most recent args is - // at the front. - std::push_heap(mrus.begin(), - mrus.end(), + // Update the mruWindows heap, so the actually most recent + // args is at the front. + std::push_heap(mruWindows.begin(), + mruWindows.end(), Remoting::implementation::CompareWindowActivatedArgs()); } // If the MRU window for the list of windows on this desktop @@ -295,8 +295,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } else { - // Here, we don't care about which desktop the window is on. We'll add this window to the list of MRU windows. - mrus.push_back(mruWindowArgs); + // Here, we don't care about which desktop the window is on. + // We'll add this window to the list of MRU windows. + mruWindows.push_back(mruWindowArgs); TraceLoggingWrite(g_hRemotingProvider, "Monarch_Collect", @@ -304,13 +305,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation "peasantID", "the ID of the MRU peasant for a desktop"), TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), - TraceLoggingBoolean(limitToCurrentDesktop, "limitToCurrentDesktop", "TODO"), + TraceLoggingBoolean(limitToCurrentDesktop, + "limitToCurrentDesktop", + "True if we should only search for a window on the current desktop"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // Update the mrus heap, so the actually most recent args is at + // Update the mruWindows heap, so the actually most recent args is at // the front. - std::push_heap(mrus.begin(), - mrus.end(), + std::push_heap(mruWindows.begin(), + mruWindows.end(), Remoting::implementation::CompareWindowActivatedArgs()); } @@ -323,24 +326,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop) { - std::vector mrus; + std::vector mruWindows; for (auto& [g, vec] : _mruPeasants) { if (vec.empty()) { continue; } - _collectMRUPeasant(limitToCurrentDesktop, mrus, vec); + _collectMRUPeasant(limitToCurrentDesktop, mruWindows, vec); } - if (!mrus.empty()) + if (!mruWindows.empty()) { TraceLoggingWrite(g_hRemotingProvider, "Monarch_getMostRecentPeasantID_Found", - TraceLoggingUInt64(mrus.begin()->PeasantID(), "peasantID", "The ID of the MRU peasant"), + TraceLoggingUInt64(mruWindows.begin()->PeasantID(), "peasantID", "The ID of the MRU peasant"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - return mrus.begin()->PeasantID(); + return mruWindows.begin()->PeasantID(); } else if (limitToCurrentDesktop) { From 5abb55a0bb4b95d3a71089693f9c9f00d7244c48 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 10 Feb 2021 16:22:27 -0600 Subject: [PATCH 62/76] it'd be a shame if I wrote unittests for a data structure that I need to get rid of entirely --- src/cascadia/Remoting/Monarch.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index dd5e66d9e41..09eb7277126 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -289,9 +289,22 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation mruWindows.end(), Remoting::implementation::CompareWindowActivatedArgs()); } - // If the MRU window for the list of windows on this desktop - // _isn't_ on the current desktop, then _none_ of the windows on - // this desktop will be. We'll break out of the loop below. + + // KNOWN BUG with useExistingSameDesktop + // 1. Open two windows on desktop A. + // 2. Drag the second to desktop B. DON'T let either window get focused. + // 3. Switch to desktop A. Again, DON'T activate window 1 + // 4. Open a new wt. + // - EXPECTED: Opens a new tab in window 1. It's on desktop A. + // - Actual: A new window is created. We see that the MRU + // window on desktop A is window 2. When we ask if window 2 + // is on desktop A, _it isn't_, and we assume the rest + // aren't either. + // + // The simplest bugfix for this would be if we could get some + // window message when we change desktops. Otherwise, we have to + // check all the windows when we determine one _isn't_ on this + // monitor. } else { From 8537a2d2cfc9ecc305922b27b986e5cf785068d6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 10 Feb 2021 17:12:12 -0600 Subject: [PATCH 63/76] this is a disaster --- src/cascadia/Remoting/Monarch.cpp | 394 ++++++++++---------- src/cascadia/Remoting/Monarch.h | 3 +- src/cascadia/Remoting/WindowActivatedArgs.h | 2 +- 3 files changed, 209 insertions(+), 190 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 09eb7277126..a753f48a164 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -170,31 +170,31 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void Monarch::_clearOldMruEntries(const uint64_t peasantID) { - // * Check all the current lists to look for this peasant. - // remove it from any where it exists. - for (auto& [g, vec] : _mruPeasants) + auto result = std::find_if(_mruPeasants.begin(), + _mruPeasants.end(), + [peasantID](auto other) { + return peasantID == other.PeasantID(); + }); + + if (result != std::end(_mruPeasants)) { - auto result = std::find_if(vec.begin(), - vec.end(), - [peasantID](auto other) { - return peasantID == other.PeasantID(); - }); - if (result != std::end(vec)) - { - vec.erase(result); + _mruPeasants.erase(result); - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_RemovedPeasantFromDesktop", - TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"), - TraceLoggingGuid(g, "desktopGuid", "The GUID of the previous desktop the window was on"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_RemovedPeasantFromDesktop", + TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"), + // TraceLoggingGuid(g, "desktopGuid", "The GUID of the previous desktop the window was on"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - std::make_heap(vec.begin(), - vec.end(), - Remoting::implementation::CompareWindowActivatedArgs()); - continue; - } + // std::make_heap(vec.begin(), + // vec.end(), + // Remoting::implementation::CompareWindowActivatedArgs()); } + + // TODO:MG Sort the vector again, now that all the old ones are gone. + std::sort(_mruPeasants.begin(), + _mruPeasants.end(), + Remoting::implementation::CompareWindowActivatedArgs()); } void Monarch::_doHandleActivatePeasant(const winrt::com_ptr& localArgs) @@ -207,15 +207,26 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // * If the current desktop doesn't have a vector, add one. const auto desktopGuid{ localArgs->DesktopID() }; - if (_mruPeasants.find(desktopGuid) == _mruPeasants.end()) - { - _mruPeasants[desktopGuid] = std::vector(); - } + // if (_mruPeasants.find(desktopGuid) == _mruPeasants.end()) + // { + // _mruPeasants[desktopGuid] = std::vector(); + // } + // * Add this args to the queue for the given desktop. - _mruPeasants[desktopGuid].push_back(*localArgs); - std::push_heap(_mruPeasants[desktopGuid].begin(), - _mruPeasants[desktopGuid].end(), - Remoting::implementation::CompareWindowActivatedArgs()); + // _mruPeasants[desktopGuid].push_back(*localArgs); + // _mruPeasants.insert(std::upper_bound(_mruPeasants.begin(), + // _mruPeasants.end(), + // *localArgs, + // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), + // *localArgs); + _mruPeasants.push_back(*localArgs); + std::sort(_mruPeasants.begin(), + _mruPeasants.end(), + Remoting::implementation::CompareWindowActivatedArgs()); + + // std::push_heap(_mruPeasants[desktopGuid].begin(), + // _mruPeasants[desktopGuid].end(), + // Remoting::implementation::CompareWindowActivatedArgs()); TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", @@ -225,171 +236,178 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - void Monarch::_collectMRUPeasant(const bool limitToCurrentDesktop, - std::vector& mruWindows, - std::vector& windowsForDesktop) - { - while (!windowsForDesktop.empty()) - { - const auto mruWindowArgs{ *windowsForDesktop.begin() }; - const auto desktopGuid = mruWindowArgs.DesktopID(); - // Trying to get the peasant will verify the peasant is still alive. - // If they aren't, the peasant will be removed from the ID->Peasant - // map, and from the the desktop->[Peasant] map as well. - // * If the peasant is dead, just try this loop again. - // * Otherwise, try and collect it. - auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; - if (!peasant) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_Collect_WasDead", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), - "peasantID", - "We thought this peasant was the MRU one, but it was actually already dead."), - TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - continue; - } - - // Here, the peasant mruWindowArgs belongs to is still alive. - if (limitToCurrentDesktop && _desktopManager) - { - // Check if this peasant is actually on this desktop. We can't - // simply get the GUID of the current desktop. We have to ask if - // the HWND is on the current desktop. - BOOL onCurrentDesktop{ false }; - - // SUCCEEDED_LOG will log if it failed, and return true if it - // SUCCEEDED - if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), - &onCurrentDesktop)) && - onCurrentDesktop) - { - mruWindows.push_back(mruWindowArgs); - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_Collect", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), - "peasantID", - "the ID of the MRU peasant for a desktop"), - TraceLoggingGuid(desktopGuid, - "desktopGuid", - "The GUID of the desktop the window is on"), - TraceLoggingBoolean(limitToCurrentDesktop, - "limitToCurrentDesktop", - "True if we should only search for a window on the current desktop"), - TraceLoggingBool(onCurrentDesktop, - "onCurrentDesktop", - "true if this window was in fact on the current desktop"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - // Update the mruWindows heap, so the actually most recent - // args is at the front. - std::push_heap(mruWindows.begin(), - mruWindows.end(), - Remoting::implementation::CompareWindowActivatedArgs()); - } - - // KNOWN BUG with useExistingSameDesktop - // 1. Open two windows on desktop A. - // 2. Drag the second to desktop B. DON'T let either window get focused. - // 3. Switch to desktop A. Again, DON'T activate window 1 - // 4. Open a new wt. - // - EXPECTED: Opens a new tab in window 1. It's on desktop A. - // - Actual: A new window is created. We see that the MRU - // window on desktop A is window 2. When we ask if window 2 - // is on desktop A, _it isn't_, and we assume the rest - // aren't either. - // - // The simplest bugfix for this would be if we could get some - // window message when we change desktops. Otherwise, we have to - // check all the windows when we determine one _isn't_ on this - // monitor. - } - else - { - // Here, we don't care about which desktop the window is on. - // We'll add this window to the list of MRU windows. - mruWindows.push_back(mruWindowArgs); - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_Collect", - TraceLoggingUInt64(mruWindowArgs.PeasantID(), - "peasantID", - "the ID of the MRU peasant for a desktop"), - TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), - TraceLoggingBoolean(limitToCurrentDesktop, - "limitToCurrentDesktop", - "True if we should only search for a window on the current desktop"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - // Update the mruWindows heap, so the actually most recent args is at - // the front. - std::push_heap(mruWindows.begin(), - mruWindows.end(), - Remoting::implementation::CompareWindowActivatedArgs()); - } - - // Exit the loop. Either we took the most recent window from the - // windows on the given desktop, or we decided not to. Either way, - // we don't need to keep looping. - break; - } - } + // void Monarch::_collectMRUPeasant(const bool limitToCurrentDesktop, + // std::vector& mruWindows, + // std::vector& windowsForDesktop) + // { + // while (!windowsForDesktop.empty()) + // { + // const auto mruWindowArgs{ *windowsForDesktop.begin() }; + // const auto desktopGuid = mruWindowArgs.DesktopID(); + // // Trying to get the peasant will verify the peasant is still alive. + // // If they aren't, the peasant will be removed from the ID->Peasant + // // map, and from the the desktop->[Peasant] map as well. + // // * If the peasant is dead, just try this loop again. + // // * Otherwise, try and collect it. + // auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; + // if (!peasant) + // { + // TraceLoggingWrite(g_hRemotingProvider, + // "Monarch_Collect_WasDead", + // TraceLoggingUInt64(mruWindowArgs.PeasantID(), + // "peasantID", + // "We thought this peasant was the MRU one, but it was actually already dead."), + // TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // continue; + // } + + // // Here, the peasant mruWindowArgs belongs to is still alive. + // if (limitToCurrentDesktop && _desktopManager) + // { + // // Check if this peasant is actually on this desktop. We can't + // // simply get the GUID of the current desktop. We have to ask if + // // the HWND is on the current desktop. + // BOOL onCurrentDesktop{ false }; + + // // SUCCEEDED_LOG will log if it failed, and return true if it + // // SUCCEEDED + // if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), + // &onCurrentDesktop)) && + // onCurrentDesktop) + // { + // mruWindows.push_back(mruWindowArgs); + + // TraceLoggingWrite(g_hRemotingProvider, + // "Monarch_Collect", + // TraceLoggingUInt64(mruWindowArgs.PeasantID(), + // "peasantID", + // "the ID of the MRU peasant for a desktop"), + // TraceLoggingGuid(desktopGuid, + // "desktopGuid", + // "The GUID of the desktop the window is on"), + // TraceLoggingBoolean(limitToCurrentDesktop, + // "limitToCurrentDesktop", + // "True if we should only search for a window on the current desktop"), + // TraceLoggingBool(onCurrentDesktop, + // "onCurrentDesktop", + // "true if this window was in fact on the current desktop"), + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // // Update the mruWindows heap, so the actually most recent + // // args is at the front. + // std::push_heap(mruWindows.begin(), + // mruWindows.end(), + // Remoting::implementation::CompareWindowActivatedArgs()); + // } + + // // KNOWN BUG with useExistingSameDesktop + // // 1. Open two windows on desktop A. + // // 2. Drag the second to desktop B. DON'T let either window get focused. + // // 3. Switch to desktop A. Again, DON'T activate window 1 + // // 4. Open a new wt. + // // - EXPECTED: Opens a new tab in window 1. It's on desktop A. + // // - Actual: A new window is created. We see that the MRU + // // window on desktop A is window 2. When we ask if window 2 + // // is on desktop A, _it isn't_, and we assume the rest + // // aren't either. + // // + // // The simplest bugfix for this would be if we could get some + // // window message when we change desktops. Otherwise, we have to + // // check all the windows when we determine one _isn't_ on this + // // monitor. + // } + // else + // { + // // Here, we don't care about which desktop the window is on. + // // We'll add this window to the list of MRU windows. + // mruWindows.push_back(mruWindowArgs); + + // TraceLoggingWrite(g_hRemotingProvider, + // "Monarch_Collect", + // TraceLoggingUInt64(mruWindowArgs.PeasantID(), + // "peasantID", + // "the ID of the MRU peasant for a desktop"), + // TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), + // TraceLoggingBoolean(limitToCurrentDesktop, + // "limitToCurrentDesktop", + // "True if we should only search for a window on the current desktop"), + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // // Update the mruWindows heap, so the actually most recent args is at + // // the front. + // std::push_heap(mruWindows.begin(), + // mruWindows.end(), + // Remoting::implementation::CompareWindowActivatedArgs()); + // } + + // // Exit the loop. Either we took the most recent window from the + // // windows on the given desktop, or we decided not to. Either way, + // // we don't need to keep looping. + // break; + // } + // } uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop) { - std::vector mruWindows; - for (auto& [g, vec] : _mruPeasants) + limitToCurrentDesktop; + if (_mruPeasants.empty()) { - if (vec.empty()) - { - continue; - } - _collectMRUPeasant(limitToCurrentDesktop, mruWindows, vec); - } - - if (!mruWindows.empty()) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_getMostRecentPeasantID_Found", - TraceLoggingUInt64(mruWindows.begin()->PeasantID(), "peasantID", "The ID of the MRU peasant"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - return mruWindows.begin()->PeasantID(); - } - else if (limitToCurrentDesktop) - { - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_getMostRecentPeasantID_NotFound_ThisDesktop", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); return 0; } - - TraceLoggingWrite(g_hRemotingProvider, - "Monarch_getMostRecentPeasantID_NotFound", - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - // We haven't yet been told the MRU peasant. Just use the first one. - // This is just gonna be a random one, but really shouldn't happen - // in practice. The WindowManager should set the MRU peasant - // immediately as soon as it creates the monarch/peasant for the - // first window. - if (_peasants.size() > 0) - { - try - { - return _peasants.begin()->second.GetID(); - } - catch (...) - { - // This shouldn't really happen. If we're the monarch, then the - // first peasant should also _be us_. So we should be able to - // get our own ID. - return 0; - } - } - return 0; + // TODO: Still need to check if that peasant is actually still alive + return _mruPeasants.begin()->PeasantID(); + // std::vector mruWindows; + // for (auto& [g, vec] : _mruPeasants) + // { + // if (vec.empty()) + // { + // continue; + // } + // _collectMRUPeasant(limitToCurrentDesktop, mruWindows, vec); + // } + + // if (!mruWindows.empty()) + // { + // TraceLoggingWrite(g_hRemotingProvider, + // "Monarch_getMostRecentPeasantID_Found", + // TraceLoggingUInt64(mruWindows.begin()->PeasantID(), "peasantID", "The ID of the MRU peasant"), + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // return mruWindows.begin()->PeasantID(); + // } + // else if (limitToCurrentDesktop) + // { + // TraceLoggingWrite(g_hRemotingProvider, + // "Monarch_getMostRecentPeasantID_NotFound_ThisDesktop", + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // return 0; + // } + + // TraceLoggingWrite(g_hRemotingProvider, + // "Monarch_getMostRecentPeasantID_NotFound", + // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + // // We haven't yet been told the MRU peasant. Just use the first one. + // // This is just gonna be a random one, but really shouldn't happen + // // in practice. The WindowManager should set the MRU peasant + // // immediately as soon as it creates the monarch/peasant for the + // // first window. + // if (_peasants.size() > 0) + // { + // try + // { + // return _peasants.begin()->second.GetID(); + // } + // catch (...) + // { + // // This shouldn't really happen. If we're the monarch, then the + // // first peasant should also _be us_. So we should be able to + // // get our own ID. + // return 0; + // } + // } + // return 0; } // Method Description: diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 0606ab59235..f7572b205cb 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -63,7 +63,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation std::unordered_map _peasants; - std::unordered_map> _mruPeasants; + // std::unordered_map> _mruPeasants; + std::vector _mruPeasants; winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop); diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h index d00a645c95d..5962cdc6eaf 100644 --- a/src/cascadia/Remoting/WindowActivatedArgs.h +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -22,7 +22,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { bool operator()(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) const { - return lhs.ActivatedTime() < rhs.ActivatedTime(); + return lhs.ActivatedTime() > rhs.ActivatedTime(); } }; struct WindowActivatedArgs : public WindowActivatedArgsT From 531577936f91a1a0b050fe53a3b087d8037fd6c5 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 11 Feb 2021 07:34:48 -0600 Subject: [PATCH 64/76] well, this bug is fixed, let's clean it up now --- src/cascadia/Remoting/Monarch.cpp | 113 +++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 24 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index a753f48a164..f5dc8295bf8 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -355,8 +355,73 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { return 0; } + + int positionInList = 0; + while (_mruPeasants.begin() + positionInList < _mruPeasants.end()) + { + auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) }; + auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; + if (!peasant) + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_Collect_WasDead", + TraceLoggingUInt64(mruWindowArgs.PeasantID(), + "peasantID", + "We thought this peasant was the MRU one, but it was actually already dead."), + TraceLoggingGuid(mruWindowArgs.DesktopID(), "desktopGuid", "The GUID of the desktop the window is on"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + // We'll go through the loop again. We removed the current one + // at positionInList, so the next one in positionInList will be + // a new, different peasant. + continue; + } + + if (limitToCurrentDesktop && _desktopManager) + { + // Check if this peasant is actually on this desktop. We can't + // simply get the GUID of the current desktop. We have to ask if + // the HWND is on the current desktop. + BOOL onCurrentDesktop{ false }; + + // SUCCEEDED_LOG will log if it failed, and return true if it + // SUCCEEDED + if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), + &onCurrentDesktop)) && + onCurrentDesktop) + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_Collect", + TraceLoggingUInt64(mruWindowArgs.PeasantID(), + "peasantID", + "the ID of the MRU peasant for a desktop"), + TraceLoggingGuid(mruWindowArgs.DesktopID(), + "desktopGuid", + "The GUID of the desktop the window is on"), + TraceLoggingBoolean(limitToCurrentDesktop, + "limitToCurrentDesktop", + "True if we should only search for a window on the current desktop"), + TraceLoggingBool(onCurrentDesktop, + "onCurrentDesktop", + "true if this window was in fact on the current desktop"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + return mruWindowArgs.PeasantID(); + } + // If theis window wasn't on the current + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_getMostRecentPeasantID_Found", + TraceLoggingUInt64(mruWindowArgs.PeasantID(), "peasantID", "The ID of the MRU peasant"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + return mruWindowArgs.PeasantID(); + } + positionInList++; + } // TODO: Still need to check if that peasant is actually still alive - return _mruPeasants.begin()->PeasantID(); + // return _mruPeasants.begin()->PeasantID(); + // return 0; // std::vector mruWindows; // for (auto& [g, vec] : _mruPeasants) // { @@ -384,30 +449,30 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // return 0; // } - // TraceLoggingWrite(g_hRemotingProvider, - // "Monarch_getMostRecentPeasantID_NotFound", - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_getMostRecentPeasantID_NotFound", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // // We haven't yet been told the MRU peasant. Just use the first one. - // // This is just gonna be a random one, but really shouldn't happen - // // in practice. The WindowManager should set the MRU peasant - // // immediately as soon as it creates the monarch/peasant for the - // // first window. - // if (_peasants.size() > 0) - // { - // try - // { - // return _peasants.begin()->second.GetID(); - // } - // catch (...) - // { - // // This shouldn't really happen. If we're the monarch, then the - // // first peasant should also _be us_. So we should be able to - // // get our own ID. - // return 0; - // } - // } - // return 0; + // We haven't yet been told the MRU peasant. Just use the first one. + // This is just gonna be a random one, but really shouldn't happen + // in practice. The WindowManager should set the MRU peasant + // immediately as soon as it creates the monarch/peasant for the + // first window. + if (_peasants.size() > 0) + { + try + { + return _peasants.begin()->second.GetID(); + } + catch (...) + { + // This shouldn't really happen. If we're the monarch, then the + // first peasant should also _be us_. So we should be able to + // get our own ID. + return 0; + } + } + return 0; } // Method Description: From f9445d758449b960770d61567b44d46e8f438466 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 11 Feb 2021 08:57:15 -0600 Subject: [PATCH 65/76] fix the tests --- .../UnitTests_Remoting/RemotingTests.cpp | 132 ++++++++---------- 1 file changed, 55 insertions(+), 77 deletions(-) diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 891fdb42932..775dce45017 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -621,8 +621,8 @@ namespace RemotingUnitTests { Log::Comment(L"Make windows on the same desktop. Validate the contents of _mruPeasants are as expected."); - const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; const auto monarch0PID = 12345u; com_ptr m0; @@ -658,10 +658,9 @@ namespace RemotingUnitTests winrt::clock().now() }; p2->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); { Log::Comment(L"Activate the first peasant, first desktop"); @@ -670,18 +669,17 @@ namespace RemotingUnitTests winrt::clock().now() }; p1->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID()); } void RemotingTests::MostRecentWindowDifferentDesktops() { Log::Comment(L"Make windows on different desktops. Validate the contents of _mruPeasants are as expected."); - const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; const auto monarch0PID = 12345u; com_ptr m0; @@ -718,10 +716,8 @@ namespace RemotingUnitTests p2->ActivateWindow(activatedArgs); } VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); Log::Comment(L"Add a third peasant"); const auto peasant3PID = 45678u; @@ -736,12 +732,10 @@ namespace RemotingUnitTests winrt::clock().now() }; p3->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID()); { Log::Comment(L"Activate the first peasant, first desktop"); @@ -750,20 +744,19 @@ namespace RemotingUnitTests winrt::clock().now() }; p1->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID()); } void RemotingTests::MostRecentWindowMoveDesktops() { - Log::Comment(L"Make windows on different desktops. Move one to another desktop. Validate the contents of _mruPeasants are as expected."); + Log::Comment(L"Make windows on different desktops. Move one to another " + L"desktop. Validate the contents of _mruPeasants are as expected."); - const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; const auto monarch0PID = 12345u; com_ptr m0; @@ -800,10 +793,8 @@ namespace RemotingUnitTests p2->ActivateWindow(activatedArgs); } VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); Log::Comment(L"Add a third peasant"); const auto peasant3PID = 45678u; @@ -818,12 +809,10 @@ namespace RemotingUnitTests winrt::clock().now() }; p3->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); + VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[1].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID()); { Log::Comment(L"Activate the first peasant, second desktop"); @@ -832,12 +821,10 @@ namespace RemotingUnitTests winrt::clock().now() }; p1->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][1].PeasantID()); + VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID()); { Log::Comment(L"Activate the third peasant, second desktop"); @@ -846,18 +833,10 @@ namespace RemotingUnitTests winrt::clock().now() }; p3->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(0u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(3u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); - - // Because the vector is internally a heap, we actually can't be sure - // what the ordering of the subsequent elements will be. We can check - // the order consistently for 2 elements. For three+, all but the first - // element will be in an indeterminate order. - // - // VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid2][1].PeasantID()); - // VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid2][2].PeasantID()); + VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[2].PeasantID()); { Log::Comment(L"Activate the second peasant, first desktop"); @@ -866,20 +845,19 @@ namespace RemotingUnitTests winrt::clock().now() }; p2->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid2].size()); - VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[guid2][0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid2][1].PeasantID()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(3u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p3->GetID(), m0->_mruPeasants[1].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[2].PeasantID()); } void RemotingTests::GetMostRecentAnyDesktop() { - Log::Comment(L"Make windows on different desktops. Confirm that getting the most recent of all windows works as expected."); + Log::Comment(L"Make windows on different desktops. Confirm that " + L"getting the most recent of all windows works as expected."); - const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; const auto monarch0PID = 12345u; com_ptr m0; @@ -944,10 +922,11 @@ namespace RemotingUnitTests void RemotingTests::MostRecentIsDead() { - Log::Comment(L"Make two windows. Activate the first, then the second. Kill the second. The most recent should be the _first_ window."); + Log::Comment(L"Make two windows. Activate the first, then the second. " + L"Kill the second. The most recent should be the _first_ window."); - const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; - const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; const auto monarch0PID = 12345u; com_ptr m0; @@ -983,10 +962,9 @@ namespace RemotingUnitTests winrt::clock().now() }; p2->ActivateWindow(activatedArgs); } - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(2u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][1].PeasantID()); + VERIFY_ARE_EQUAL(2u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p2->GetID(), m0->_mruPeasants[0].PeasantID()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[1].PeasantID()); Log::Comment(L"Kill peasant 2"); RemotingTests::_killPeasant(m0, p2->GetID()); @@ -996,8 +974,8 @@ namespace RemotingUnitTests Log::Comment(L"Peasant 2 should not be in the monarch at all anymore"); VERIFY_ARE_EQUAL(1u, m0->_peasants.size()); VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); - VERIFY_ARE_EQUAL(1u, m0->_mruPeasants[guid1].size()); - VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[guid1][0].PeasantID()); + VERIFY_ARE_EQUAL(1u, m0->_mruPeasants.size()); + VERIFY_ARE_EQUAL(p1->GetID(), m0->_mruPeasants[0].PeasantID()); } } From 5df4c16a5c8ef4fa57bb176705d99bb7e135fe21 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 11 Feb 2021 10:16:07 -0600 Subject: [PATCH 66/76] Remove a bunch of dead code, add comments --- src/cascadia/Remoting/Monarch.cpp | 154 ++++++------------------------ src/cascadia/Remoting/Monarch.h | 3 - 2 files changed, 28 insertions(+), 129 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f5dc8295bf8..cac5c383c0d 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -114,7 +114,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } // Method Description: - // - Lookup a peasant by its ID. + // - Lookup a peasant by its ID. If the peasant has died, this will also + // remove the peasant from our list of peasants. // Arguments: // - peasantID: The ID Of the peasant to find // Return Value: @@ -139,13 +140,29 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Remove the peasant from the list of peasants _peasants.erase(peasantID); - // Remove the peasant from the MRU windows mapping. They're dead. + // Remove the peasant from the list of MRU windows. They're dead. // They can't be the MRU anymore. _clearOldMruEntries(peasantID); return nullptr; } } + // Method Description: + // - Handler for the `Peasant::WindowActivated` event. We'll make a in-proc + // copy of the WindowActivatedArgs from the peasant. That way, we won't + // need to worry about the origin process dying when working with the + // WindowActivatedArgs. + // - If the peasant process dies while we're making this copy, then we'll + // just log it and do nothing. We certainly don't want to track a dead + // peasant. + // - We'll pass that copy of the WindowActivatedArgs to + // _doHandleActivatePeasant, which will actually insert the + // WindowActivatedArgs into the list we're using to track the most recent + // peasants. + // Arguments: + // - args: the WindowActivatedArgs describing when and where the peasant was activated. + // Return Value: + // - void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args) { // Start by making a local copy of these args. It's easier for us if our @@ -158,6 +175,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation args.Hwnd(), args.DesktopID(), args.ActivatedTime()); + // This method will actually do the hard work _doHandleActivatePeasant(localArgs); } catch (...) @@ -168,6 +186,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } + // Method Description: + // - Helper for removing a peasant from the list of MRU peasants. We want to + // do this both when the peasant dies, and also when the peasant is newly + // activated (so that we don't leave an old entry for it in the list). + // Arguments: + // - peasantID: The ID of the peasant to remove from the MRU list + // Return Value: + // - void Monarch::_clearOldMruEntries(const uint64_t peasantID) { auto result = std::find_if(_mruPeasants.begin(), @@ -185,10 +211,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"), // TraceLoggingGuid(g, "desktopGuid", "The GUID of the previous desktop the window was on"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - // std::make_heap(vec.begin(), - // vec.end(), - // Remoting::implementation::CompareWindowActivatedArgs()); } // TODO:MG Sort the vector again, now that all the old ones are gone. @@ -207,10 +229,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // * If the current desktop doesn't have a vector, add one. const auto desktopGuid{ localArgs->DesktopID() }; - // if (_mruPeasants.find(desktopGuid) == _mruPeasants.end()) - // { - // _mruPeasants[desktopGuid] = std::vector(); - // } // * Add this args to the queue for the given desktop. // _mruPeasants[desktopGuid].push_back(*localArgs); @@ -224,10 +242,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mruPeasants.end(), Remoting::implementation::CompareWindowActivatedArgs()); - // std::push_heap(_mruPeasants[desktopGuid].begin(), - // _mruPeasants[desktopGuid].end(), - // Remoting::implementation::CompareWindowActivatedArgs()); - TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", TraceLoggingUInt64(localArgs->PeasantID(), "peasantID", "the ID of the activated peasant"), @@ -236,118 +250,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - // void Monarch::_collectMRUPeasant(const bool limitToCurrentDesktop, - // std::vector& mruWindows, - // std::vector& windowsForDesktop) - // { - // while (!windowsForDesktop.empty()) - // { - // const auto mruWindowArgs{ *windowsForDesktop.begin() }; - // const auto desktopGuid = mruWindowArgs.DesktopID(); - // // Trying to get the peasant will verify the peasant is still alive. - // // If they aren't, the peasant will be removed from the ID->Peasant - // // map, and from the the desktop->[Peasant] map as well. - // // * If the peasant is dead, just try this loop again. - // // * Otherwise, try and collect it. - // auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; - // if (!peasant) - // { - // TraceLoggingWrite(g_hRemotingProvider, - // "Monarch_Collect_WasDead", - // TraceLoggingUInt64(mruWindowArgs.PeasantID(), - // "peasantID", - // "We thought this peasant was the MRU one, but it was actually already dead."), - // TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // continue; - // } - - // // Here, the peasant mruWindowArgs belongs to is still alive. - // if (limitToCurrentDesktop && _desktopManager) - // { - // // Check if this peasant is actually on this desktop. We can't - // // simply get the GUID of the current desktop. We have to ask if - // // the HWND is on the current desktop. - // BOOL onCurrentDesktop{ false }; - - // // SUCCEEDED_LOG will log if it failed, and return true if it - // // SUCCEEDED - // if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), - // &onCurrentDesktop)) && - // onCurrentDesktop) - // { - // mruWindows.push_back(mruWindowArgs); - - // TraceLoggingWrite(g_hRemotingProvider, - // "Monarch_Collect", - // TraceLoggingUInt64(mruWindowArgs.PeasantID(), - // "peasantID", - // "the ID of the MRU peasant for a desktop"), - // TraceLoggingGuid(desktopGuid, - // "desktopGuid", - // "The GUID of the desktop the window is on"), - // TraceLoggingBoolean(limitToCurrentDesktop, - // "limitToCurrentDesktop", - // "True if we should only search for a window on the current desktop"), - // TraceLoggingBool(onCurrentDesktop, - // "onCurrentDesktop", - // "true if this window was in fact on the current desktop"), - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - // // Update the mruWindows heap, so the actually most recent - // // args is at the front. - // std::push_heap(mruWindows.begin(), - // mruWindows.end(), - // Remoting::implementation::CompareWindowActivatedArgs()); - // } - - // // KNOWN BUG with useExistingSameDesktop - // // 1. Open two windows on desktop A. - // // 2. Drag the second to desktop B. DON'T let either window get focused. - // // 3. Switch to desktop A. Again, DON'T activate window 1 - // // 4. Open a new wt. - // // - EXPECTED: Opens a new tab in window 1. It's on desktop A. - // // - Actual: A new window is created. We see that the MRU - // // window on desktop A is window 2. When we ask if window 2 - // // is on desktop A, _it isn't_, and we assume the rest - // // aren't either. - // // - // // The simplest bugfix for this would be if we could get some - // // window message when we change desktops. Otherwise, we have to - // // check all the windows when we determine one _isn't_ on this - // // monitor. - // } - // else - // { - // // Here, we don't care about which desktop the window is on. - // // We'll add this window to the list of MRU windows. - // mruWindows.push_back(mruWindowArgs); - - // TraceLoggingWrite(g_hRemotingProvider, - // "Monarch_Collect", - // TraceLoggingUInt64(mruWindowArgs.PeasantID(), - // "peasantID", - // "the ID of the MRU peasant for a desktop"), - // TraceLoggingGuid(desktopGuid, "desktopGuid", "The GUID of the desktop the window is on"), - // TraceLoggingBoolean(limitToCurrentDesktop, - // "limitToCurrentDesktop", - // "True if we should only search for a window on the current desktop"), - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - // // Update the mruWindows heap, so the actually most recent args is at - // // the front. - // std::push_heap(mruWindows.begin(), - // mruWindows.end(), - // Remoting::implementation::CompareWindowActivatedArgs()); - // } - - // // Exit the loop. Either we took the most recent window from the - // // windows on the given desktop, or we decided not to. Either way, - // // we don't need to keep looping. - // break; - // } - // } - uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop) { limitToCurrentDesktop; diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index f7572b205cb..aaae5446802 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -72,9 +72,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); void _doHandleActivatePeasant(const winrt::com_ptr& args); - void _collectMRUPeasant(const bool limitToCurrentDesktop, - std::vector& mrus, - std::vector& windowsForDesktop); void _clearOldMruEntries(const uint64_t peasantID); friend class RemotingUnitTests::RemotingTests; From 8aefb6c17985392f598bab136a3a4fd6a337b32e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 11 Feb 2021 10:17:02 -0600 Subject: [PATCH 67/76] Somehow, this breaks the world? I'm just going to revert all of it --- src/cascadia/Remoting/Monarch.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cac5c383c0d..8f446f25be5 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -196,27 +196,27 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - void Monarch::_clearOldMruEntries(const uint64_t peasantID) { - auto result = std::find_if(_mruPeasants.begin(), - _mruPeasants.end(), + // Search for the WindowActivatedArgs from this peasant + auto result = std::find_if(std::begin(_mruPeasants), //_mruPeasants.begin(), + std::end(_mruPeasants), //_mruPeasants.end(), [peasantID](auto other) { return peasantID == other.PeasantID(); }); if (result != std::end(_mruPeasants)) + // if (result != _mruPeasants.end()) { _mruPeasants.erase(result); TraceLoggingWrite(g_hRemotingProvider, "Monarch_RemovedPeasantFromDesktop", TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"), - // TraceLoggingGuid(g, "desktopGuid", "The GUID of the previous desktop the window was on"), + TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - // TODO:MG Sort the vector again, now that all the old ones are gone. - std::sort(_mruPeasants.begin(), - _mruPeasants.end(), - Remoting::implementation::CompareWindowActivatedArgs()); + // The list was previously in-order, and it's still in-order without + // this entry. } void Monarch::_doHandleActivatePeasant(const winrt::com_ptr& localArgs) @@ -232,11 +232,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // * Add this args to the queue for the given desktop. // _mruPeasants[desktopGuid].push_back(*localArgs); - // _mruPeasants.insert(std::upper_bound(_mruPeasants.begin(), + // _mruPeasants.insert(std::lower_bound(_mruPeasants.begin(), // _mruPeasants.end(), // *localArgs, - // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), + // // Remoting::implementation::CompareWindowActivatedArgs()), + // // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), + // [](const auto& first, const auto& second) { return first.ActivatedTime() >= second.ActivatedTime(); }), + // // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), + // // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), // *localArgs); + _mruPeasants.push_back(*localArgs); std::sort(_mruPeasants.begin(), _mruPeasants.end(), From 8d05c5d6250c676ee3420016d6ade1d15c869cf0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 11 Feb 2021 10:17:18 -0600 Subject: [PATCH 68/76] Revert "Somehow, this breaks the world? I'm just going to revert all of it" This reverts commit 8aefb6c17985392f598bab136a3a4fd6a337b32e. --- src/cascadia/Remoting/Monarch.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 8f446f25be5..cac5c383c0d 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -196,27 +196,27 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - void Monarch::_clearOldMruEntries(const uint64_t peasantID) { - // Search for the WindowActivatedArgs from this peasant - auto result = std::find_if(std::begin(_mruPeasants), //_mruPeasants.begin(), - std::end(_mruPeasants), //_mruPeasants.end(), + auto result = std::find_if(_mruPeasants.begin(), + _mruPeasants.end(), [peasantID](auto other) { return peasantID == other.PeasantID(); }); if (result != std::end(_mruPeasants)) - // if (result != _mruPeasants.end()) { _mruPeasants.erase(result); TraceLoggingWrite(g_hRemotingProvider, "Monarch_RemovedPeasantFromDesktop", TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"), - TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"), + // TraceLoggingGuid(g, "desktopGuid", "The GUID of the previous desktop the window was on"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } - // The list was previously in-order, and it's still in-order without - // this entry. + // TODO:MG Sort the vector again, now that all the old ones are gone. + std::sort(_mruPeasants.begin(), + _mruPeasants.end(), + Remoting::implementation::CompareWindowActivatedArgs()); } void Monarch::_doHandleActivatePeasant(const winrt::com_ptr& localArgs) @@ -232,16 +232,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // * Add this args to the queue for the given desktop. // _mruPeasants[desktopGuid].push_back(*localArgs); - // _mruPeasants.insert(std::lower_bound(_mruPeasants.begin(), + // _mruPeasants.insert(std::upper_bound(_mruPeasants.begin(), // _mruPeasants.end(), // *localArgs, - // // Remoting::implementation::CompareWindowActivatedArgs()), - // // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), - // [](const auto& first, const auto& second) { return first.ActivatedTime() >= second.ActivatedTime(); }), - // // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), - // // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), + // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), // *localArgs); - _mruPeasants.push_back(*localArgs); std::sort(_mruPeasants.begin(), _mruPeasants.end(), From 36bb19b3e1e5fa0f100c53bff5cf7fc942c1564b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 11 Feb 2021 10:58:48 -0600 Subject: [PATCH 69/76] ho boy am I daft --- src/cascadia/Remoting/Monarch.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index cac5c383c0d..62789333c69 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -204,16 +204,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation if (result != std::end(_mruPeasants)) { - _mruPeasants.erase(result); - TraceLoggingWrite(g_hRemotingProvider, "Monarch_RemovedPeasantFromDesktop", TraceLoggingUInt64(peasantID, "peasantID", "The ID of the peasant"), - // TraceLoggingGuid(g, "desktopGuid", "The GUID of the previous desktop the window was on"), + TraceLoggingGuid(result->DesktopID(), "desktopGuid", "The GUID of the previous desktop the window was on"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); + + _mruPeasants.erase(result); } - // TODO:MG Sort the vector again, now that all the old ones are gone. + // Sort the vector again, now that all the old ones are gone. std::sort(_mruPeasants.begin(), _mruPeasants.end(), Remoting::implementation::CompareWindowActivatedArgs()); From f96dad42e22e32e0f4b120e762775e02de6a030c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 11 Feb 2021 11:57:59 -0600 Subject: [PATCH 70/76] Ready for the prime time --- src/cascadia/Remoting/Monarch.cpp | 138 +++++++++--------- src/cascadia/Remoting/Monarch.h | 1 - .../UnitTests_Remoting/RemotingTests.cpp | 2 +- 3 files changed, 69 insertions(+), 72 deletions(-) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 62789333c69..f553a556dc3 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -212,13 +212,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _mruPeasants.erase(result); } - - // Sort the vector again, now that all the old ones are gone. - std::sort(_mruPeasants.begin(), - _mruPeasants.end(), - Remoting::implementation::CompareWindowActivatedArgs()); } + // Method Description: + // - Actually handle inserting the WindowActivatedArgs into our list of MRU windows. + // Arguments: + // - localArgs: an in-proc WindowActivatedArgs that we should add to our list of MRU windows. + // Return Value: + // - void Monarch::_doHandleActivatePeasant(const winrt::com_ptr& localArgs) { const auto newLastActiveTime = localArgs->ActivatedTime().time_since_epoch().count(); @@ -230,17 +231,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // * If the current desktop doesn't have a vector, add one. const auto desktopGuid{ localArgs->DesktopID() }; - // * Add this args to the queue for the given desktop. - // _mruPeasants[desktopGuid].push_back(*localArgs); - // _mruPeasants.insert(std::upper_bound(_mruPeasants.begin(), - // _mruPeasants.end(), - // *localArgs, - // [](const auto& first, const auto& second) { return first.ActivatedTime() < second.ActivatedTime(); }), - // *localArgs); - _mruPeasants.push_back(*localArgs); - std::sort(_mruPeasants.begin(), - _mruPeasants.end(), - Remoting::implementation::CompareWindowActivatedArgs()); + // * Add this args list. By using lower_bound with insert, we can get it + // into exactly the right spot, without having to re-sort the whole + // array. + _mruPeasants.insert(std::lower_bound(_mruPeasants.begin(), + _mruPeasants.end(), + *localArgs, + [](const auto& first, const auto& second) { return first.ActivatedTime() > second.ActivatedTime(); }), + *localArgs); TraceLoggingWrite(g_hRemotingProvider, "Monarch_SetMostRecentPeasant", @@ -250,19 +248,63 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); } + // Method Description: + // - Retrieves the ID of the MRU peasant window. If requested, will limit + // the search to windows that are on the current desktop. + // Arguments: + // - limitToCurrentDesktop: if true, only return the MRU peasant that's + // actually on the current desktop. + // Return Value: + // - the ID of the most recent peasant, otherwise 0 if we could not find one. uint64_t Monarch::_getMostRecentPeasantID(const bool limitToCurrentDesktop) { - limitToCurrentDesktop; if (_mruPeasants.empty()) { + // We haven't yet been told the MRU peasant. Just use the first one. + // This is just gonna be a random one, but really shouldn't happen + // in practice. The WindowManager should set the MRU peasant + // immediately as soon as it creates the monarch/peasant for the + // first window. + if (_peasants.size() > 0) + { + try + { + return _peasants.begin()->second.GetID(); + } + catch (...) + { + // This shouldn't really happen. If we're the monarch, then the + // first peasant should also _be us_. So we should be able to + // get our own ID. + return 0; + } + } + + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_getMostRecentPeasantID_NoPeasants", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); return 0; } + // Here, there's at least one MRU peasant. + // + // We're going to iterate over these peasants toll we find one that both: + // 1. Is alive + // 2. Meets our selection criteria (do we care if it is on this desktop?) + // + // If the peasant is dead, then we'll remove it, and try the next one. + // Once we find one that's alive, we'll either: + // * check if we only want a peasant on the current desktop, and if so, + // check if this peasant is on the current desktop. + // - If it isn't on the current desktop, we'll loop again, on the + // following peasant. + // * If we don't care, then we'll just return that one. + int positionInList = 0; while (_mruPeasants.begin() + positionInList < _mruPeasants.end()) { - auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) }; - auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; + const auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) }; + const auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; if (!peasant) { TraceLoggingWrite(g_hRemotingProvider, @@ -285,8 +327,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // the HWND is on the current desktop. BOOL onCurrentDesktop{ false }; - // SUCCEEDED_LOG will log if it failed, and return true if it - // SUCCEEDED + // SUCCEEDED_LOG will log if it failed, and return true if it SUCCEEDED if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), &onCurrentDesktop)) && onCurrentDesktop) @@ -308,7 +349,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); return mruWindowArgs.PeasantID(); } - // If theis window wasn't on the current + // If this window wasn't on the current desktop, another one + // might be. We'll increment positionInList below, and try + // again. } else { @@ -321,59 +364,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } positionInList++; } - // TODO: Still need to check if that peasant is actually still alive - // return _mruPeasants.begin()->PeasantID(); - // return 0; - // std::vector mruWindows; - // for (auto& [g, vec] : _mruPeasants) - // { - // if (vec.empty()) - // { - // continue; - // } - // _collectMRUPeasant(limitToCurrentDesktop, mruWindows, vec); - // } - - // if (!mruWindows.empty()) - // { - // TraceLoggingWrite(g_hRemotingProvider, - // "Monarch_getMostRecentPeasantID_Found", - // TraceLoggingUInt64(mruWindows.begin()->PeasantID(), "peasantID", "The ID of the MRU peasant"), - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - - // return mruWindows.begin()->PeasantID(); - // } - // else if (limitToCurrentDesktop) - // { - // TraceLoggingWrite(g_hRemotingProvider, - // "Monarch_getMostRecentPeasantID_NotFound_ThisDesktop", - // TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // return 0; - // } + // Here, we've checked all the windows, and none of them was both alive + // and the most recent (on this desktop). Just return 0 - the caller + // will use this to create a new window. TraceLoggingWrite(g_hRemotingProvider, "Monarch_getMostRecentPeasantID_NotFound", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE)); - // We haven't yet been told the MRU peasant. Just use the first one. - // This is just gonna be a random one, but really shouldn't happen - // in practice. The WindowManager should set the MRU peasant - // immediately as soon as it creates the monarch/peasant for the - // first window. - if (_peasants.size() > 0) - { - try - { - return _peasants.begin()->second.GetID(); - } - catch (...) - { - // This shouldn't really happen. If we're the monarch, then the - // first peasant should also _be us_. So we should be able to - // get our own ID. - return 0; - } - } return 0; } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index aaae5446802..8c62e14866b 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -63,7 +63,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation std::unordered_map _peasants; - // std::unordered_map> _mruPeasants; std::vector _mruPeasants; winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID); diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 775dce45017..6c9e503ad2e 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -122,7 +122,7 @@ namespace RemotingUnitTests if (arguments.size() > 0) { const auto index = std::stoi(arguments.at(0).c_str()); - args.ResultTargetWindow(index); + args.ResultTargetWindow(index >= 0 ? index : -1); } } From e15c3450f4a28e175a3337992008900c570476eb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 17 Feb 2021 12:01:00 -0600 Subject: [PATCH 71/76] Rename the setting --- doc/cascadia/profiles.schema.json | 10 ++++++++++ src/cascadia/Remoting/Monarch.cpp | 10 +++++----- src/cascadia/TerminalApp/AppLogic.cpp | 8 ++++---- .../TerminalSettingsModel/GlobalAppSettings.idl | 4 ++-- .../TerminalSettingsSerializationHelpers.h | 2 +- src/cascadia/UnitTests_Remoting/RemotingTests.cpp | 2 +- src/inc/WindowingBehavior.h | 4 ++-- 7 files changed, 25 insertions(+), 15 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 9a43234411a..c2881541f42 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -812,6 +812,16 @@ "type": "string" } ] + }, + "windowingBehavior": { + "default": "useNew", + "description": "Controls how new terminal instances attach to existing windows.", + "enum": [ + "useNew", + "useAny", + "useAnyExisting" + ], + "type": "string" } }, "required": [ diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index f553a556dc3..65f3e3822b8 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -410,23 +410,23 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // that goes with it. Alternatively, if we were given a magic windowing // constant, we can use that to look up an appropriate peasant. if (targetWindow >= 0 || - targetWindow == WindowingBehaviorUseExistingSameDesktop || - targetWindow == WindowingBehaviorUseExisting) + targetWindow == WindowingBehaviorUseExisting || + targetWindow == WindowingBehaviorUseAnyExisting) { uint64_t windowID = 0; switch (targetWindow) { case WindowingBehaviorUseCurrent: - case WindowingBehaviorUseExistingSameDesktop: + case WindowingBehaviorUseExisting: // TODO:projects/5 for now, just use the MRU window. Technically, - // UseExistingSameDesktop and UseCurrent are different. + // UseExisting and UseCurrent are different. // UseCurrent implies that we should try to do the WT_SESSION // lookup to find the window that spawned this process (then // fall back to sameDesktop if we can't find a match). For now, // it's good enough to just try to find a match on this desktop. windowID = _getMostRecentPeasantID(true); break; - case WindowingBehaviorUseExisting: + case WindowingBehaviorUseAnyExisting: windowID = _getMostRecentPeasantID(false); break; default: diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index d29f0a2a88e..93ac85a9e6b 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1196,9 +1196,9 @@ namespace winrt::TerminalApp::implementation // Return Value: // - 0: We should handle the args "in the current window". // - WindowingBehaviorUseNew: We should handle the args in a new window - // - WindowingBehaviorUseExistingSameDesktop: We should handle the args "in + // - WindowingBehaviorUseExisting: We should handle the args "in // the current window ON THIS DESKTOP" - // - WindowingBehaviorUseExisting: We should handle the args "in the current + // - WindowingBehaviorUseAnyExisting: We should handle the args "in the current // window ON ANY DESKTOP" // - anything else: We should handle the commandline in the window with the given ID. int32_t AppLogic::FindTargetWindow(array_view args) @@ -1225,10 +1225,10 @@ namespace winrt::TerminalApp::implementation { case WindowingMode::UseNew: return WindowingBehaviorUseNew; - case WindowingMode::UseExistingSameDesktop: - return WindowingBehaviorUseExistingSameDesktop; case WindowingMode::UseExisting: return WindowingBehaviorUseExisting; + case WindowingMode::UseAnyExisting: + return WindowingBehaviorUseAnyExisting; default: return WindowingBehaviorUseNew; } diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 1bf1d1ba1af..8af520bc80a 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -36,8 +36,8 @@ namespace Microsoft.Terminal.Settings.Model enum WindowingMode { UseNew, + UseAnyExisting, UseExisting, - UseExistingSameDesktop, }; [default_interface] runtimeclass GlobalAppSettings { @@ -160,6 +160,6 @@ namespace Microsoft.Terminal.Settings.Model Boolean HasWindowingBehavior(); void ClearWindowingBehavior(); - WindowingMode WindowingBehavior(); + WindowingMode WindowingBehavior; } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 397f68155a3..bb75d499d05 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -436,7 +436,7 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode) { JSON_MAPPINGS(3) = { pair_type{ "useNew", ValueType::UseNew }, + pair_type{ "useAnyExisting", ValueType::UseAnyExisting }, pair_type{ "useExisting", ValueType::UseExisting }, - pair_type{ "useExistingSameDesktop", ValueType::UseExistingSameDesktop }, }; }; diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 6c9e503ad2e..81856c5422f 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -611,7 +611,7 @@ namespace RemotingUnitTests // TODO:projects/5 // - // In order to test WindowingBehaviorUseExistingSameDesktop, we'll have to + // In order to test WindowingBehaviorUseExisting, we'll have to // create our own IVirtualDesktopManager implementation that can be subbed // in for testing. We can't _actually_ create HWNDs as a part of the test // and move them to different desktops. Instead, we'll have to create a stub diff --git a/src/inc/WindowingBehavior.h b/src/inc/WindowingBehavior.h index 1a93238ece7..0a6b55bd34e 100644 --- a/src/inc/WindowingBehavior.h +++ b/src/inc/WindowingBehavior.h @@ -6,5 +6,5 @@ Licensed under the MIT license. #pragma once constexpr int64_t WindowingBehaviorUseCurrent{ 0 }; constexpr int64_t WindowingBehaviorUseNew{ -1 }; -constexpr int64_t WindowingBehaviorUseExistingSameDesktop{ -2 }; -constexpr int64_t WindowingBehaviorUseExisting{ -3 }; +constexpr int64_t WindowingBehaviorUseExisting{ -2 }; +constexpr int64_t WindowingBehaviorUseAnyExisting{ -3 }; From 570375d349dc3cc1662040766d7ed9746a77380a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 17 Feb 2021 12:04:22 -0600 Subject: [PATCH 72/76] Add windowingBehavior to the SUI @carlos-zamora why the heck doesn't this work? It works to change the value of the setting, but it does _not_ work on the initial load of the Launch page. It always selects UseNew. But if I change the value, DONT SAVE, navigate away from the page, then back to the page, then it loads correctly. What gives? --- src/cascadia/TerminalSettingsEditor/Launch.cpp | 1 + src/cascadia/TerminalSettingsEditor/Launch.h | 1 + src/cascadia/TerminalSettingsEditor/Launch.idl | 8 ++++++-- src/cascadia/TerminalSettingsEditor/Launch.xaml | 7 +++++++ .../Resources/en-US/Resources.resw | 15 +++++++++++++++ .../TerminalSettingsModel/EnumMappings.cpp | 1 + src/cascadia/TerminalSettingsModel/EnumMappings.h | 1 + .../TerminalSettingsModel/EnumMappings.idl | 1 + 8 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/Launch.cpp b/src/cascadia/TerminalSettingsEditor/Launch.cpp index f1b42c9b0aa..b2bbaedd4bf 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.cpp +++ b/src/cascadia/TerminalSettingsEditor/Launch.cpp @@ -18,6 +18,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation InitializeComponent(); INITIALIZE_BINDABLE_ENUM_SETTING(LaunchMode, LaunchMode, LaunchMode, L"Globals_LaunchMode", L"Content"); + INITIALIZE_BINDABLE_ENUM_SETTING(WindowingBehavior, WindowingMode, WindowingMode, L"Globals_WindowingBehavior", L"Content"); } void Launch::OnNavigatedTo(const NavigationEventArgs& e) diff --git a/src/cascadia/TerminalSettingsEditor/Launch.h b/src/cascadia/TerminalSettingsEditor/Launch.h index 0cede6a9fbc..33fb30cb63c 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.h +++ b/src/cascadia/TerminalSettingsEditor/Launch.h @@ -31,6 +31,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation GETSET_PROPERTY(Editor::LaunchPageNavigationState, State, nullptr); GETSET_BINDABLE_ENUM_SETTING(LaunchMode, Model::LaunchMode, State().Settings().GlobalSettings, LaunchMode); + GETSET_BINDABLE_ENUM_SETTING(WindowingBehavior, Model::WindowingMode, State().Settings().GlobalSettings, WindowingBehavior); }; } diff --git a/src/cascadia/TerminalSettingsEditor/Launch.idl b/src/cascadia/TerminalSettingsEditor/Launch.idl index 8823a80fb86..e2a1382e0d3 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.idl +++ b/src/cascadia/TerminalSettingsEditor/Launch.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 { @@ -18,6 +18,10 @@ namespace Microsoft.Terminal.Settings.Editor IInspectable CurrentDefaultProfile; IInspectable CurrentLaunchMode; - Windows.Foundation.Collections.IObservableVector LaunchModeList { get; }; + IObservableVector LaunchModeList { get; }; + + + IInspectable CurrentWindowingBehavior; + IObservableVector WindowingBehaviorList { get; }; } } diff --git a/src/cascadia/TerminalSettingsEditor/Launch.xaml b/src/cascadia/TerminalSettingsEditor/Launch.xaml index c9c2a090d43..caa28bdba94 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.xaml +++ b/src/cascadia/TerminalSettingsEditor/Launch.xaml @@ -78,6 +78,13 @@ the MIT License. See LICENSE in the project root for license information. --> ItemsSource="{x:Bind LaunchModeList}" ItemTemplate="{StaticResource EnumRadioButtonTemplate}"/> + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index bb8ac02dc49..a2ff690e21a 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -232,6 +232,21 @@ Maximized + + Windowing behavior + + + Controls how new terminal instances attach to existing windows + + + Create a new window + + + Create new tabs in the most recently used window + + + Create new tabs in the most recently used window on this desktop + These settings may be useful for troubleshooting an issue, however they will impact your performance. diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp index 7058df611f9..f28e2a44c50 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.cpp +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.cpp @@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation DEFINE_ENUM_MAP(Model::LaunchMode, LaunchMode); DEFINE_ENUM_MAP(Model::TabSwitcherMode, TabSwitcherMode); DEFINE_ENUM_MAP(Microsoft::Terminal::TerminalControl::CopyFormat, CopyFormat); + DEFINE_ENUM_MAP(Model::WindowingMode, WindowingMode); // Profile Settings DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.h b/src/cascadia/TerminalSettingsModel/EnumMappings.h index fd299107f77..69aba3f9014 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.h +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.h @@ -31,6 +31,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static winrt::Windows::Foundation::Collections::IMap LaunchMode(); static winrt::Windows::Foundation::Collections::IMap TabSwitcherMode(); static winrt::Windows::Foundation::Collections::IMap CopyFormat(); + static winrt::Windows::Foundation::Collections::IMap WindowingMode(); // Profile Settings static winrt::Windows::Foundation::Collections::IMap CloseOnExitMode(); diff --git a/src/cascadia/TerminalSettingsModel/EnumMappings.idl b/src/cascadia/TerminalSettingsModel/EnumMappings.idl index 86ec268fcbf..d9ff512a7b3 100644 --- a/src/cascadia/TerminalSettingsModel/EnumMappings.idl +++ b/src/cascadia/TerminalSettingsModel/EnumMappings.idl @@ -13,6 +13,7 @@ namespace Microsoft.Terminal.Settings.Model static Windows.Foundation.Collections.IMap LaunchMode { get; }; static Windows.Foundation.Collections.IMap TabSwitcherMode { get; }; static Windows.Foundation.Collections.IMap CopyFormat { get; }; + static Windows.Foundation.Collections.IMap WindowingMode { get; }; // Profile Settings static Windows.Foundation.Collections.IMap CloseOnExitMode { get; }; From 257d92b0ac7e797a6c41306b7b947596624cbb59 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 18 Feb 2021 05:47:33 -0600 Subject: [PATCH 73/76] most of the PR feedback --- doc/cascadia/profiles.schema.json | 4 ++-- src/cascadia/Remoting/Monarch.cpp | 18 +++++++++--------- src/cascadia/Remoting/Peasant.idl | 1 - src/cascadia/Remoting/WindowActivatedArgs.h | 6 ++++++ src/cascadia/Remoting/WindowManager.h | 1 - .../TerminalSettingsEditor/Launch.xaml | 2 +- .../Resources/en-US/Resources.resw | 10 +++++----- .../GlobalAppSettings.cpp | 1 + src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- 9 files changed, 25 insertions(+), 20 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index c2881541f42..ef2c505eede 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -815,10 +815,10 @@ }, "windowingBehavior": { "default": "useNew", - "description": "Controls how new terminal instances attach to existing windows.", + "description": "Controls how new terminal instances attach to existing windows. \"useNew\" will create a new window always. \"useExisting\" will create new tabs in the most recently used window on this desktop, and \"useAnyExisting\" will create tabs in the most recent window on any desktop.", "enum": [ "useNew", - "useAny", + "useExisting", "useAnyExisting" ], "type": "string" diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 65f3e3822b8..0eca93e48a7 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -171,10 +171,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::com_ptr localArgs{ nullptr }; try { - localArgs = winrt::make_self(args.PeasantID(), - args.Hwnd(), - args.DesktopID(), - args.ActivatedTime()); + localArgs = winrt::make_self(args); // This method will actually do the hard work _doHandleActivatePeasant(localArgs); } @@ -198,7 +195,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation { auto result = std::find_if(_mruPeasants.begin(), _mruPeasants.end(), - [peasantID](auto other) { + [peasantID](auto&& other) { return peasantID == other.PeasantID(); }); @@ -288,7 +285,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // Here, there's at least one MRU peasant. // - // We're going to iterate over these peasants toll we find one that both: + // We're going to iterate over these peasants until we find one that both: // 1. Is alive // 2. Meets our selection criteria (do we care if it is on this desktop?) // @@ -299,9 +296,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - If it isn't on the current desktop, we'll loop again, on the // following peasant. // * If we don't care, then we'll just return that one. - + // + // We're not just using an iterator because the contents of the list + // might change while we're iterating here (if the peasant is dead we'll + // remove it from the list). int positionInList = 0; - while (_mruPeasants.begin() + positionInList < _mruPeasants.end()) + while (_mruPeasants.cbegin() + positionInList < _mruPeasants.cend()) { const auto mruWindowArgs{ *(_mruPeasants.begin() + positionInList) }; const auto peasant{ _getPeasant(mruWindowArgs.PeasantID()) }; @@ -328,7 +328,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation BOOL onCurrentDesktop{ false }; // SUCCEEDED_LOG will log if it failed, and return true if it SUCCEEDED - if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop((HWND)mruWindowArgs.Hwnd(), + if (SUCCEEDED_LOG(_desktopManager->IsWindowOnCurrentVirtualDesktop(reinterpret_cast(mruWindowArgs.Hwnd()), &onCurrentDesktop)) && onCurrentDesktop) { diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 71f090bf93d..8d092d3d423 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -11,7 +11,6 @@ namespace Microsoft.Terminal.Remoting String[] Commandline { get; set; }; String CurrentDirectory(); - }; runtimeclass WindowActivatedArgs diff --git a/src/cascadia/Remoting/WindowActivatedArgs.h b/src/cascadia/Remoting/WindowActivatedArgs.h index 5962cdc6eaf..19ea914ef89 100644 --- a/src/cascadia/Remoting/WindowActivatedArgs.h +++ b/src/cascadia/Remoting/WindowActivatedArgs.h @@ -46,6 +46,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::guid desktopID, winrt::Windows::Foundation::DateTime timestamp) : WindowActivatedArgs(peasantID, 0, desktopID, timestamp){}; + + WindowActivatedArgs(const Remoting::WindowActivatedArgs& other) : + _PeasantID{ other.PeasantID() }, + _Hwnd{ other.Hwnd() }, + _DesktopID{ other.DesktopID() }, + _ActivatedTime{ other.ActivatedTime() } {}; }; } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index a2db645b7b4..bc257ef06b6 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -59,7 +59,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool _performElection(); void _createPeasantThread(); void _waitOnMonarchThread(); - void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); }; diff --git a/src/cascadia/TerminalSettingsEditor/Launch.xaml b/src/cascadia/TerminalSettingsEditor/Launch.xaml index caa28bdba94..ab0b60d1862 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.xaml +++ b/src/cascadia/TerminalSettingsEditor/Launch.xaml @@ -82,7 +82,7 @@ the MIT License. See LICENSE in the project root for license information. --> diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index a2ff690e21a..5a4c3ad4d1e 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -233,19 +233,19 @@ Maximized - Windowing behavior + New windows open in... - Controls how new terminal instances attach to existing windows + Controls how new terminal instances attach to existing windows. - Create a new window + a new window - Create new tabs in the most recently used window + the most recently used window - Create new tabs in the most recently used window on this desktop + the most recently used window on this desktop These settings may be useful for troubleshooting an issue, however they will impact your performance. diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 7e5badca7b3..fc0debc5c01 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -119,6 +119,7 @@ winrt::com_ptr GlobalAppSettings::Copy() const globals->_DisableAnimations = _DisableAnimations; globals->_StartupActions = _StartupActions; globals->_FocusFollowMouse = _FocusFollowMouse; + globals->_WindowingBehavior = _WindowingBehavior; globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile; globals->_validDefaultProfile = _validDefaultProfile; diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index c70039f7d38..24fb7cdc062 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -575,7 +575,7 @@ GUID AppHost::_CurrentDesktopGuid() GUID currentDesktopGuid{ 0 }; try { - auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); + const auto manager = winrt::create_instance(__uuidof(VirtualDesktopManager)); if (manager) { LOG_IF_FAILED(manager->GetWindowDesktopId(_window->GetHandle(), ¤tDesktopGuid)); From 1bf6754781f61d09cfa31de22b8b8a8da643c08c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 18 Feb 2021 06:02:59 -0600 Subject: [PATCH 74/76] Move the header to make Dustin happy --- src/cascadia/Remoting/Monarch.cpp | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 2 +- src/{ => cascadia}/inc/WindowingBehavior.h | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{ => cascadia}/inc/WindowingBehavior.h (100%) diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 0eca93e48a7..628b8d01a37 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT license. #include "pch.h" -#include "WindowingBehavior.h" +#include "../inc/WindowingBehavior.h" #include "Monarch.h" #include "CommandlineArgs.h" #include "FindTargetWindowArgs.h" diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 93ac85a9e6b..d2feb0a6ce6 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -3,7 +3,7 @@ #include "pch.h" #include "AppLogic.h" -#include "WindowingBehavior.h" +#include "../inc/WindowingBehavior.h" #include "AppLogic.g.cpp" #include diff --git a/src/inc/WindowingBehavior.h b/src/cascadia/inc/WindowingBehavior.h similarity index 100% rename from src/inc/WindowingBehavior.h rename to src/cascadia/inc/WindowingBehavior.h From cb311d8bfa299c02045d9c1d0b988317e16eb5ac Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 18 Feb 2021 15:03:27 -0600 Subject: [PATCH 75/76] some PR nits --- doc/cascadia/profiles.schema.json | 2 +- src/cascadia/TerminalApp/AppLogic.cpp | 3 +-- .../TerminalSettingsEditor/Resources/en-US/Resources.resw | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index ef2c505eede..d98c1659f56 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -815,7 +815,7 @@ }, "windowingBehavior": { "default": "useNew", - "description": "Controls how new terminal instances attach to existing windows. \"useNew\" will create a new window always. \"useExisting\" will create new tabs in the most recently used window on this desktop, and \"useAnyExisting\" will create tabs in the most recent window on any desktop.", + "description": "Controls how new terminal instances attach to existing windows. \"useNew\" will always create a new window. \"useExisting\" will create new tabs in the most recently used window on this virtual desktop, and \"useAnyExisting\" will create tabs in the most recent window on any desktop.", "enum": [ "useNew", "useExisting", diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index d2feb0a6ce6..6dea350e75a 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1223,12 +1223,11 @@ namespace winrt::TerminalApp::implementation const auto windowingBehavior = _settings.GlobalSettings().WindowingBehavior(); switch (windowingBehavior) { - case WindowingMode::UseNew: - return WindowingBehaviorUseNew; case WindowingMode::UseExisting: return WindowingBehaviorUseExisting; case WindowingMode::UseAnyExisting: return WindowingBehaviorUseAnyExisting; + case WindowingMode::UseNew: default: return WindowingBehaviorUseNew; } diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 5a4c3ad4d1e..3e8f125e6bc 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -234,6 +234,7 @@ New windows open in... + This text is a fragment of a sentence that indicates the options below it, in a user interface. It is a header, but it should be read like a sentence that ends with one of {Globals_WindowingBehaviorUseNew.Content}, {Globals_WindowingBehaviorUseAnyExisting.Content}, or {Globals_WindowingBehaviorUseExisting.Content}. Controls how new terminal instances attach to existing windows. From fa96b5ddf2fa3894a8d469d82ddf3614e08f9934 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 19 Feb 2021 14:44:30 -0600 Subject: [PATCH 76/76] words r hard --- .../Resources/en-US/Resources.resw | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 3e8f125e6bc..6fb0f772d6b 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -233,20 +233,19 @@ Maximized - New windows open in... - This text is a fragment of a sentence that indicates the options below it, in a user interface. It is a header, but it should be read like a sentence that ends with one of {Globals_WindowingBehaviorUseNew.Content}, {Globals_WindowingBehaviorUseAnyExisting.Content}, or {Globals_WindowingBehaviorUseExisting.Content}. + New instance behavior Controls how new terminal instances attach to existing windows. - a new window + Create a new window - the most recently used window + Attach to the most recently used window - the most recently used window on this desktop + Attach to the most recently used window on this desktop These settings may be useful for troubleshooting an issue, however they will impact your performance.