Compare commits

..

15 Commits

Author SHA1 Message Date
Dustin L. Howett
d1e080cf63 Migrate spelling-0.0.21 changes from main 2021-01-05 11:17:53 -06:00
Dustin L. Howett
6711e4398c Migrate spelling-0.0.19 changes from main 2021-01-05 11:17:53 -06:00
Mike Griese
4a536a39e7 This is going to turn into a disaster.
We still need to reference MUX
    to get mdmerge happy, but we can't roll that up without rolling up the DLL
    and now we'll get duplicate types in WT.vcxproj. GAH. We'll need to have
    Remoting accept a callback to say "I encountered a commandline, someone,
    deal with it?", then have apphost register for that callback. So the flow
    is monarch->AppHost->AppLogic
2021-01-05 11:17:53 -06:00
Mike Griese
d4dbd530e9 change the namespace 2021-01-05 08:55:40 -06:00
Mike Griese
b3601ab8d3 some project cleanup 2021-01-05 08:43:51 -06:00
Mike Griese
ca6a5fb537 Move the commandline args to their own project, so Remoting can reference them too 2021-01-05 08:26:16 -06:00
Mike Griese
c08889585b last commit before the holidays 2020-12-18 15:21:53 -06:00
Mike Griese
5939636182 Oh yea actually remove the thing 2020-12-18 12:48:23 -06:00
Mike Griese
f978a9c52c it's hard to believe he's gone / were it so easy 2020-12-18 12:45:29 -06:00
Mike Griese
e1402d834f This seems to work to create a thread listening for the monarch, and safely tear it down. It _seems_ like it. 2020-12-18 09:29:45 -06:00
Mike Griese
e101efd11d pr nits 2020-12-18 07:12:12 -06:00
Mike Griese
d08e65ce03 _final_ final cleanup for review 2020-12-17 14:33:50 -06:00
Mike Griese
b4fe1bffbf Final cleanup for review 2020-12-17 14:26:06 -06:00
Mike Griese
0103331987 cleanup Peasant for review 2020-12-17 12:49:27 -06:00
Mike Griese
9c6eac4e86 Clean up a lot for review 2020-12-17 12:39:59 -06:00
33 changed files with 827 additions and 295 deletions

View File

@@ -232,9 +232,16 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_TerminalApp", "sr
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommandlineArgs", "src\cascadia\CommandlineArgs\CommandlineArgs.vcxproj", "{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
EndProjectSection
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}
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA} = {2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}
{CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED} = {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}
EndProjectSection
@@ -1607,6 +1614,30 @@ Global
{CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x64.Build.0 = Release|x64
{CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x86.ActiveCfg = Release|Win32
{CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x86.Build.0 = Release|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.AuditMode|x64.ActiveCfg = AuditMode|x64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|Any CPU.ActiveCfg = Debug|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|ARM64.Build.0 = Debug|ARM64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|x64.ActiveCfg = Debug|x64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|x64.Build.0 = Debug|x64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|x86.ActiveCfg = Debug|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Debug|x86.Build.0 = Debug|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|Any CPU.ActiveCfg = Release|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|ARM64.ActiveCfg = Release|ARM64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|ARM64.Build.0 = Release|ARM64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|x64.ActiveCfg = Release|x64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|x64.Build.0 = Release|x64
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|x86.ActiveCfg = Release|Win32
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA}.Release|x86.Build.0 = Release|Win32
{CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|Any CPU.ActiveCfg = Debug|Win32
{CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|ARM64.ActiveCfg = Release|ARM64
{CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|DotNet_x64Test.ActiveCfg = Debug|Win32
@@ -2289,6 +2320,7 @@ Global
{84848BFA-931D-42CE-9ADF-01EE54DE7890} = {59840756-302F-44DF-AA47-441A9D673202}
{376FE273-6B84-4EB5-8B30-8DE9D21B022C} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
{2658E9B0-BD84-48B7-BB5E-3D18F4DF07BA} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
{48D21369-3D7B-4431-9967-24E81292CF63} = {05500DEF-2294-41E3-AF9A-24E580B82836}

View File

@@ -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 (

View File

@@ -2,14 +2,12 @@
// Licensed under the MIT license.
#include "pch.h"
#include "AppLogic.h"
#include "AppCommandlineArgs.h"
#include "../types/inc/utils.hpp"
#include <LibraryResources.h>
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace TerminalApp;
using namespace CommandlineArgs;
// Either a ; at the start of a line, or a ; preceded by any non-\ char.
const std::wregex AppCommandlineArgs::_commandDelimiterRegex{ LR"(^;|[^\\];)" };
@@ -687,7 +685,7 @@ std::optional<winrt::Microsoft::Terminal::Settings::Model::LaunchMode> AppComman
// - 0 if the commandline was successfully parsed
int AppCommandlineArgs::ParseArgs(winrt::array_view<const winrt::hstring>& args)
{
auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args);
auto commands = ::CommandlineArgs::AppCommandlineArgs::BuildCommands(args);
for (auto& cmdBlob : commands)
{

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
#pragma once
#include <CLI11/CLI11.hpp>
#include "Commandline.h"
#ifdef UNIT_TESTING
@@ -12,12 +13,12 @@ namespace TerminalAppLocalTests
};
#endif
namespace TerminalApp
namespace CommandlineArgs
{
class AppCommandlineArgs;
};
class TerminalApp::AppCommandlineArgs final
class CommandlineArgs::AppCommandlineArgs final
{
public:
static constexpr std::string_view NixHelpFlag{ "-?" };

View File

@@ -4,7 +4,7 @@
#include "pch.h"
#include "Commandline.h"
using namespace TerminalApp;
using namespace CommandlineArgs;
size_t Commandline::Argc() const
{

View File

@@ -23,12 +23,12 @@ namespace TerminalAppLocalTests
{
class CommandlineTest;
};
namespace TerminalApp
namespace CommandlineArgs
{
class Commandline;
};
class TerminalApp::Commandline
class CommandlineArgs::Commandline
{
public:
static constexpr std::wstring_view Delimiter{ L";" };

View File

@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{2658e9b0-bd84-48b7-bb5e-3d18f4df07ba}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>Microsoft.Terminal.CommandlineArgs</RootNamespace>
<ProjectName>CommandlineArgs</ProjectName>
<TargetName>CommandlineArgs</TargetName>
<WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion>
<ConfigurationType>StaticLibrary</ConfigurationType>
<SubSystem>Console</SubSystem>
<OpenConsoleUniversalApp>true</OpenConsoleUniversalApp>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<!-- For CLI11: It uses dynamic_cast to cast types around, which depends
on being compiled with RTTI (/GR). -->
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
</ItemDefinitionGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="AppCommandlineArgs.h" />
<ClInclude Include="Commandline.h" />
<ClInclude Include="pch.h" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="AppCommandlineArgs.cpp" />
<ClCompile Include="Commandline.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<!-- ========================= Project References ======================== -->
<ItemGroup>
<!--
the packaging project won't recurse through our dependencies, you have to
make sure that if you add a cppwinrt dependency to any of these projects,
you also update all the consumers
-->
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WinRTUtils\WinRTUtils.vcxproj">
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<!-- For whatever reason, we can't include the TerminalControl and
TerminalSettings projects' winmds via project references. So we'll have to
manually include the winmds as References below -->
</ItemGroup>
<PropertyGroup>
<!-- This is a hack to get the ARM64 CI build working. See
https://github.com/Microsoft/msbuild/issues/3746 - it looks like MsBuild
just has a bug in it.-->
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>Warning</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
</PropertyGroup>
<ItemGroup>
<!-- Manually add references to each of our dependent winmds. Mark them as
private=false and CopyLocalSatelliteAssemblies=false, so that we don't
propagate them upwards (which can make referencing this project result in
duplicate type definitions)-->
<!-- Despite this lib only _really_ depending on the settings model, we need
to reference Connection, Control, and MUX here, because they have types that
TSM depends on, and the midl compiler will be mad if it can't find them. -->
<Reference Include="Microsoft.Terminal.TerminalConnection">
<HintPath>$(OpenConsoleCommonOutDir)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
<Reference Include="Microsoft.Terminal.TerminalControl">
<HintPath>$(OpenConsoleCommonOutDir)TerminalControl\Microsoft.Terminal.TerminalControl.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
<Reference Include="Microsoft.Terminal.Settings.Model">
<HintPath>$(OpenConsoleCommonOutDir)Microsoft.Terminal.Settings.Model\Microsoft.Terminal.Settings.Model.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
</ItemGroup>
<!-- ====================== Compiler & Linker Flags ===================== -->
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<!-- Manually disable unreachable code warning, because jconcpp has a ton of that. -->
<DisableSpecificWarnings>4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>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}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.5.0-prerelease.201202003\build\native\Microsoft.UI.Xaml.targets'))" />
</Target>
<!-- <Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" /> -->
</Project>

View File

@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"

View File

@@ -0,0 +1,53 @@
// 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 <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 <wil/cppwinrt.h>
#include <unknwn.h>
#include <hstring.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
// You might think "we're not doing any UI in this lib", and you'd be right.
// However, we need this for Windows.UI.Color
#include <winrt/windows.ui.h>
// Including TraceLogging essentials for the binary
#include <TraceLoggingProvider.h>
#include <winmeta.h>
TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
#include <telemetry/ProjectTelemetry.h>
#include <TraceLoggingActivity.h>
#include <shellapi.h>
#include <shobjidl_core.h>
#include <winrt/Microsoft.Terminal.Settings.Model.h>
#include <CLI11/CLI11.hpp>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"

View File

@@ -8,9 +8,10 @@ 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 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 winrt::hstring> const& value)
{

View File

@@ -14,6 +14,16 @@
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<ItemDefinitionGroup>
<ClCompile>
<!-- For CLI11: It uses dynamic_cast to cast types around, which depends
on being compiled with RTTI (/GR). We need this in this project, because
we're including the CLI11 header via the CommandlineArgs project. -->
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
</ItemDefinitionGroup>
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="Monarch.h">
@@ -74,17 +84,51 @@
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\CommandlineArgs\CommandlineArgs.vcxproj" >
<Private>true</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
<!-- Make sure to set ReferenceOutputAssembly=false, or we'll try and
MDMERGE with MUX from the CommandlineArgs output, and get duplicate type
errors everywhere. -->
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<!-- This is going to turn into a disaster. We still need to reference MUX
to get mdmerge happy, but we can't roll that up without rolling up the DLL
and now we'll get duplicate types in WT.vcxproj. GAH. We'll need to have
Remoting accept a callback to say "I encountered a commandline, someone,
deal with it?", then have apphost register for that callback. So the flow
is monarch->AppHost->AppLogic -->
</ProjectReference>
<!-- For whatever reason, we can't include the TerminalControl and
TerminalSettings projects' winmds via project references. So we'll have to
manually include the winmds as References below -->
<Reference Include="Microsoft.Terminal.TerminalConnection">
<HintPath>$(OpenConsoleCommonOutDir)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
<Reference Include="Microsoft.Terminal.TerminalControl">
<HintPath>$(OpenConsoleCommonOutDir)TerminalControl\Microsoft.Terminal.TerminalControl.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
<Reference Include="Microsoft.Terminal.Settings.Model">
<HintPath>$(OpenConsoleCommonOutDir)Microsoft.Terminal.Settings.Model\Microsoft.Terminal.Settings.Model.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Private>false</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</Reference>
</ItemGroup>
<!-- ====================== Compiler & Linker Flags ===================== -->
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..;$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
<!-- Manually disable unreachable code warning, because jconcpp has a ton of that. -->
<DisableSpecificWarnings>4702;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;user32.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>

View File

@@ -1,11 +1,13 @@
#include "pch.h"
#include "Monarch.h"
#include "CommandlineArgs.h"
#include "../CommandlineArgs/AppCommandlineArgs.h"
#include "Monarch.g.cpp"
#include "../../types/inc/utils.hpp"
using namespace winrt;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Windows::Foundation;
using namespace ::Microsoft::Console;
@@ -15,6 +17,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 }
{
@@ -22,7 +27,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
Monarch::~Monarch()
{
printf("~Monarch()\n");
}
uint64_t Monarch::GetPID()
@@ -30,152 +34,116 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return _ourPID;
}
uint64_t Monarch::AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant)
// 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: 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;
}
// 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:
// - <none>
void Monarch::_peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
if (auto peasant{ sender.try_as<winrt::Microsoft::Terminal::Remoting::Peasant>() })
// TODO:projects/5 Pass the desktop and timestamp of when the window was
// activated in `args`.
if (auto peasant{ sender.try_as<Remoting::Peasant>() })
{
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;
try
{
auto peasantSearch = _peasants.find(peasantID);
auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second;
if (maybeThePeasant)
{
maybeThePeasant.GetPID();
}
return maybeThePeasant;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
// Remove the peasant from the list of peasants
_peasants.erase(peasantID);
return nullptr;
}
}
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)
// 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 themselves.
// Arguments:
// - <none>
// Return Value:
// - <none>
bool Monarch::ProposeCommandline(const Remoting::CommandlineArgs& /*args*/)
{
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);
}
// 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 themselves.
bool Monarch::ProposeCommandline(array_view<const winrt::hstring> 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");
CommandlineArgs::AppCommandlineArgs argParser;
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<const winrt::hstring> 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<implementation::CommandlineArgs>(argsNoWindow, cwd);
mruPeasant.ExecuteCommandline(*eventArgs);
createNewWindow = false;
}
}
else
{
if (auto otherPeasant = _getPeasant(sessionId))
{
auto eventArgs = winrt::make_self<implementation::CommandlineArgs>(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<implementation::CommandlineArgs>(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;
return true;
}
}

View File

@@ -34,9 +34,7 @@ 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<const winrt::hstring> args, winrt::hstring cwd);
bool ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
private:
Monarch(const uint64_t testPID);

View File

@@ -7,6 +7,6 @@ namespace Microsoft.Terminal.Remoting
UInt64 GetPID();
UInt64 AddPeasant(IPeasant peasant);
Boolean ProposeCommandline(String[] args, String cwd);
Boolean ProposeCommandline(CommandlineArgs args);
};
}

View File

@@ -11,7 +11,15 @@ using namespace ::Microsoft::Console;
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
Peasant::Peasant()
Peasant::Peasant() :
_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 }
{
}
@@ -26,36 +34,27 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
uint64_t Peasant::GetPID()
{
return GetCurrentProcessId();
return _ourPID;
}
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);
// 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;

View File

@@ -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<Peasant>
@@ -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<const winrt::hstring> _args;
// winrt::hstring _cwd;
friend class RemotingUnitTests::RemotingTests;
};
}

View File

@@ -15,9 +15,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
WindowManager::WindowManager()
{
_monarchWaitInterrupt.create();
// Register with COM as a server for the Monarch class
_registerAsMonarch();
_createMonarch();
// Instantiate an instance of the Monarch. This may or may not be in-proc!
_createMonarchAndCallbacks();
}
WindowManager::~WindowManager()
{
// IMPORTANT! Tear down the registration as soon as we exit. If we're not a
@@ -26,32 +31,39 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// monarch!
CoRevokeClassObject(_registrationHostClass);
_registrationHostClass = 0;
_monarchWaitInterrupt.SetEvent();
if (_electionThread.joinable())
{
_electionThread.join();
}
}
void WindowManager::ProposeCommandline(array_view<const winrt::hstring> 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
// 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);
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<implementation::CommandlineArgs>(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;
// 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_.
}
bool WindowManager::ShouldCreateWindow()
@@ -76,12 +88,27 @@ 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<Remoting::Monarch>(Monarch_clsid,
CLSCTX_LOCAL_SERVER);
}
void WindowManager::_createMonarchAndCallbacks()
{
_createMonarch();
const auto isKing = _areWeTheKing();
if (!isKing)
{
return;
}
// Here, we're the king!
// 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();
@@ -93,18 +120,91 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
{
auto p = winrt::make_self<Remoting::implementation::Peasant>();
_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);
return _peasant;
}
bool WindowManager::_electionNight2020()
{
_createMonarchAndCallbacks();
// Tell the new monarch who we are. We might be that monarch!
_monarch.AddPeasant(_peasant);
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] {
_waitOnMonarchThread();
});
}
void WindowManager::_waitOnMonarchThread()
{
HANDLE waits[2];
waits[1] = _monarchWaitInterrupt.get();
bool exitRequested = false;
while (!exitRequested)
{
wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
static_cast<DWORD>(_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)
{
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()
{
return _peasant;

View File

@@ -7,12 +7,12 @@
namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct WindowManager : public WindowManagerT<WindowManager>
struct WindowManager final : public WindowManagerT<WindowManager>
{
WindowManager();
~WindowManager();
void ProposeCommandline(array_view<const winrt::hstring> args, const winrt::hstring cwd);
void ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args);
bool ShouldCreateWindow();
winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow();
@@ -23,10 +23,19 @@ 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();
void _createMonarchAndCallbacks();
bool _areWeTheKing();
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant();
bool _electionNight2020();
void _createPeasantThread();
void _waitOnMonarchThread();
};
}

View File

@@ -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();
};
}

View File

@@ -44,5 +44,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hSettingsModelProvider);
#include <shellapi.h>
#include <winrt/Microsoft.Terminal.Settings.Model.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"

View File

@@ -17,7 +17,6 @@ using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
using namespace ::TerminalApp;
namespace winrt
{

View File

@@ -17,7 +17,7 @@ using namespace winrt::Windows::System;
using namespace winrt::Microsoft::Terminal;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace ::TerminalApp;
using namespace ::CommandlineArgs;
namespace winrt
{
@@ -1113,7 +1113,7 @@ namespace winrt::TerminalApp::implementation
int32_t AppLogic::ExecuteCommandline(array_view<const winrt::hstring> args)
{
::TerminalApp::AppCommandlineArgs appArgs;
::CommandlineArgs::AppCommandlineArgs appArgs;
auto result = appArgs.ParseArgs(args);
if (result == 0)
{

View File

@@ -85,7 +85,7 @@ namespace winrt::TerminalApp::implementation
std::atomic<bool> _settingsReloadQueued{ false };
::TerminalApp::AppCommandlineArgs _appArgs;
::CommandlineArgs::AppCommandlineArgs _appArgs;
int _ParseArgs(winrt::array_view<const hstring>& args);
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult);

View File

@@ -5,7 +5,7 @@
#include "FilteredCommand.h"
#include "CommandPalette.g.h"
#include "AppCommandlineArgs.h"
#include "../CommandlineArgs/AppCommandlineArgs.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
// fwdecl unittest classes
@@ -130,7 +130,7 @@ namespace winrt::TerminalApp::implementation
static constexpr int CommandLineHistoryLength = 10;
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandLineHistory{ nullptr };
::TerminalApp::AppCommandlineArgs _appArgs;
::CommandlineArgs::AppCommandlineArgs _appArgs;
friend class TerminalAppLocalTests::TabTests;
};

View File

@@ -17,7 +17,7 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
using namespace winrt::TerminalApp;
using namespace TerminalApp;
static const int PaneBorderSize = 2;
static const int CombinedPaneBorderSize = 2 * PaneBorderSize;

View File

@@ -25,7 +25,8 @@
<ClCompile>
<!-- For CLI11: It uses dynamic_cast to cast types around, which depends
on being compiled with RTTI (/GR). -->
on being compiled with RTTI (/GR). We need this in this project, because
we're including the CLI11 header via the CommandlineArgs project. -->
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
</ItemDefinitionGroup>
@@ -71,8 +72,6 @@
<ItemGroup>
<ClInclude Include="ActionPaletteItem.h" />
<ClInclude Include="App.base.h" />
<ClInclude Include="AppCommandlineArgs.h" />
<ClInclude Include="Commandline.h" />
<ClInclude Include="CommandLinePaletteItem.h" />
<ClInclude Include="Jumplist.h" />
<ClInclude Include="MinMaxCloseControl.h">
@@ -144,8 +143,6 @@
<ClCompile Include="ActionPaletteItem.cpp" />
<ClCompile Include="CommandLinePaletteItem.cpp" />
<ClCompile Include="init.cpp" />
<ClCompile Include="AppCommandlineArgs.cpp" />
<ClCompile Include="Commandline.cpp" />
<ClCompile Include="Jumplist.cpp" />
<ClCompile Include="MinMaxCloseControl.cpp">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
@@ -288,6 +285,14 @@
you also update all the consumers
-->
<ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\CommandlineArgs\CommandlineArgs.vcxproj" >
<Private>true</Private>
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
<!-- Make sure to set ReferenceOutputAssembly=false, or we'll try and
MDMERGE with MUX from the CommandlineArgs output, and get duplicate type
errors everywhere. -->
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WinRTUtils\WinRTUtils.vcxproj">
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>

View File

@@ -2698,7 +2698,7 @@ namespace winrt::TerminalApp::implementation
// - an empty list if we failed to parse, otherwise a list of actions to execute.
std::vector<ActionAndArgs> TerminalPage::ConvertExecuteCommandlineToActions(const ExecuteCommandlineArgs& args)
{
::TerminalApp::AppCommandlineArgs appArgs;
::CommandlineArgs::AppCommandlineArgs appArgs;
if (appArgs.ParseArgs(args) == 0)
{
return appArgs.GetStartupActions();

View File

@@ -10,7 +10,7 @@
#include <winrt/Microsoft.Terminal.TerminalControl.h>
#include "AppCommandlineArgs.h"
#include "../CommandlineArgs/AppCommandlineArgs.h"
static constexpr uint32_t DefaultRowsToScroll{ 3 };
static constexpr std::wstring_view TabletInputServiceKey{ L"TabletInputService" };

View File

@@ -76,7 +76,5 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
#include <winrt/Windows.UI.Popups.h>
#include <CLI11/CLI11.hpp>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"

View File

@@ -12,27 +12,77 @@ 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<Remoting::implementation::Monarch> 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<Remoting::implementation::Peasant> name; \
name.attach(&_local_##name##); \
auto cleanup_##name## = wil::scope_exit([&]() { name.detach(); });
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, winrt::Microsoft::Terminal::Remoting::IPeasant>
{
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)
END_TEST_CLASS()
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<Remoting::implementation::Monarch>& m,
const uint64_t peasantID);
};
void RemotingTests::_killPeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID)
{
if (peasantID <= 0)
{
return;
}
com_ptr<DeadPeasant> tombstone;
tombstone.attach(new DeadPeasant());
m->_peasants[peasantID] = *tombstone;
}
void RemotingTests::CreateMonarch()
{
auto m1 = winrt::make_self<Remoting::implementation::Monarch>();
@@ -52,4 +102,194 @@ namespace RemotingUnitTests
L"A Monarch with an explicit PID should use the one we provided");
}
void RemotingTests::CreatePeasant()
{
auto p1 = winrt::make_self<Remoting::implementation::Peasant>();
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");
}
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<Remoting::implementation::Peasant>();
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<Remoting::implementation::Peasant> 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<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> 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<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> 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<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
com_ptr<Remoting::implementation::Monarch> 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<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> 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());
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);
VERIFY_IS_NOT_NULL(maybeP2);
VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID());
auto maybeP1 = m0->_getPeasant(1);
VERIFY_IS_NULL(maybeP1);
VERIFY_ARE_EQUAL(1u, m0->_peasants.size());
}
}

View File

@@ -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"
@@ -14,6 +13,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;
@@ -30,50 +30,16 @@ AppHost::AppHost() noexcept :
{
_logic = _app.Logic(); // get a ref to app's logic
{
std::vector<winrt::hstring> 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<LPWSTR*, decltype(&::LocalFree), ::LocalFree> 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?
}
// 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
// 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)
{
return;
}
_useNonClientArea = _logic.GetShowTabsInTitlebar();
if (_useNonClientArea)
@@ -137,9 +103,35 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable&
}
}
void _buildArgsFromCommandline(std::vector<winrt::hstring>& 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<LPWSTR*, decltype(&::LocalFree), ::LocalFree> 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 arguments 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,19 @@ void AppHost::SetTaskbarProgress(const winrt::Windows::Foundation::IInspectable&
// - <none>
void AppHost::_HandleCommandlineArgs()
{
std::vector<winrt::hstring> args;
_buildArgsFromCommandline(args);
std::wstring cwd{ wil::GetCurrentDirectoryW<std::wstring>() };
Remoting::CommandlineArgs eventArgs{ { args }, { cwd } };
_windowManager.ProposeCommandline(eventArgs);
_shouldCreateWindow = _windowManager.ShouldCreateWindow();
if (!_shouldCreateWindow)
{
return;
}
if (auto peasant{ _windowManager.CurrentWindow() })
{
if (auto args{ peasant.InitialArgs() })
@@ -176,42 +181,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<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(commandline, &argc) };
// if (argv)
// {
// std::vector<winrt::hstring> 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:
@@ -534,26 +516,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());

View File

@@ -26,7 +26,6 @@ private:
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 @@ private:
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);
};

View File

@@ -6,7 +6,7 @@
<test name="terminalApp" type="unit" binary="UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll" />
<test name="localTerminalApp" type="unit" runInHostApp="true" binary="TerminalApp.LocalTests.dll" />
<test name="localSettingsModel" type="unit" runInHostApp="true" binary="SettingsModel.LocalTests.dll" />
<test name="unitRemoting" type="unit" runInHostApp="true" binary="Remoting.UnitTests.dll" />
<test name="unitRemoting" type="unit" binary="Remoting.UnitTests.dll" />
<test name="interactivityWin32" type="unit" binary="Conhost.Interactivity.Win32.Unit.Tests.dll" />
<test name="terminal" type="unit" binary="ConParser.Unit.Tests.dll" />
<test name="adapter" type="unit" binary="ConAdapter.Unit.Tests.dll" />