diff --git a/OpenConsole.sln b/OpenConsole.sln
index 493cba041da..7afd974a612 100644
--- a/OpenConsole.sln
+++ b/OpenConsole.sln
@@ -336,6 +336,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalTestNetCore", "s
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevate-shim", "src\cascadia\ElevateShim\elevate-shim.vcxproj", "{416FD703-BAA7-4F6E-9361-64F550EC8FCA}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.Settings.Editor", "src\cascadia\TerminalSettingsEditor\Microsoft.Terminal.Settings.Editor.vcxproj", "{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-082C-4476-9F33-94B339494076} = {CA5CAD1A-082C-4476-9F33-94B339494076}
@@ -400,6 +402,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests",
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-ms-win-core-synch-l1-2-0", "src\api-ms-win-core-synch-l1-2-0\api-ms-win-core-synch-l1-2-0.vcxproj", "{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|Any CPU = AuditMode|Any CPU
@@ -2803,6 +2807,43 @@ Global
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x64.ActiveCfg = AuditMode|x64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x64.Build.0 = AuditMode|x64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x86.ActiveCfg = AuditMode|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|x86.Build.0 = AuditMode|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|ARM.ActiveCfg = Debug|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|ARM64.Build.0 = Debug|ARM64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x64.ActiveCfg = Debug|x64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x64.Build.0 = Debug|x64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x86.ActiveCfg = Debug|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Debug|x86.Build.0 = Debug|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|Any CPU.ActiveCfg = Release|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|ARM.ActiveCfg = Release|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|ARM64.ActiveCfg = Release|ARM64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|ARM64.Build.0 = Release|ARM64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.ActiveCfg = Release|x64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|Any CPU.ActiveCfg = Release|x64
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|Any CPU.Build.0 = Release|x64
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|Any CPU.Deploy.0 = Release|x64
@@ -3403,7 +3444,7 @@ Global
{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}
- {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} = {59840756-302F-44DF-AA47-441A9D673202}
+ {CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}
{B0AC39D6-7B40-49A9-8202-58549BAE1FB1} = {59840756-302F-44DF-AA47-441A9D673202}
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{A22EC5F6-7851-4B88-AC52-47249D437A52} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
@@ -3416,10 +3457,11 @@ Global
{D3EF7B96-CD5E-47C9-B9A9-136259563033} = {04170EEF-983A-4195-BFEF-2321E5E38A1E}
{95B136F9-B238-490C-A7C5-5843C1FECAC4} = {05500DEF-2294-41E3-AF9A-24E580B82836}
{024052DE-83FB-4653-AEA4-90790D29D5BD} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
- {067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202}
+ {067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}
{6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
{1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {4DAF0299-495E-4CD1-A982-9BAC16A45932}
- {506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202}
+ {506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}
+ {416FD703-BAA7-4F6E-9361-64F550EC8FCA} = {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C}
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32} = {77875138-BB08-49F9-8BB1-409C2150E0E1}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {77875138-BB08-49F9-8BB1-409C2150E0E1}
{CA5CAD1A-082C-4476-9F33-94B339494076} = {77875138-BB08-49F9-8BB1-409C2150E0E1}
@@ -3438,6 +3480,7 @@ Global
{C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
{F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E}
{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
+ {61901E80-E97D-4D61-A9BB-E8F2FDA8B40C} = {59840756-302F-44DF-AA47-441A9D673202}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj
index ba722f24665..c898bfd0b22 100644
--- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj
+++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj
@@ -68,6 +68,7 @@
+
diff --git a/src/cascadia/ElevateShim/elevate-shim.cpp b/src/cascadia/ElevateShim/elevate-shim.cpp
new file mode 100644
index 00000000000..30670043f03
--- /dev/null
+++ b/src/cascadia/ElevateShim/elevate-shim.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include
+#include
+
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#include
+#include
+#include
+
+// BODGY
+//
+// If we try to do this in the Terminal itself, then there's a bunch of weird
+// things that can go wrong and prevent the elevated Terminal window from
+// getting created. Specifically, if the origin Terminal exits right away after
+// spawning the elevated WT, then ShellExecute might not successfully complete
+// the elevation. What's even more, the Terminal will mysteriously crash
+// somewhere in XAML land.
+//
+// To mitigate this, the Terminal will call into us with the commandline it
+// wants elevated. We'll hang around until ShellExecute is finished, so that the
+// process can successfully elevate.
+
+#pragma warning(suppress : 26461) // we can't change the signature of wWinMain
+int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR pCmdLine, int)
+{
+ // All of the args passed to us (something like `new-tab -p {guid}`) are in
+ // pCmdLine
+
+ // Get the path to WindowsTerminal.exe, which should live next to us.
+ std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) };
+ // Swap elevate-shim.exe for WindowsTerminal.exe
+ module.replace_filename(L"WindowsTerminal.exe");
+
+ // Go!
+ SHELLEXECUTEINFOW seInfo{ 0 };
+ seInfo.cbSize = sizeof(seInfo);
+ seInfo.fMask = SEE_MASK_DEFAULT;
+ seInfo.lpVerb = L"runas"; // This asks the shell to elevate the process
+ seInfo.lpFile = module.c_str(); // This is `...\WindowsTerminal.exe`
+ seInfo.lpParameters = pCmdLine; // This is `new-tab -p {guid}`
+ seInfo.nShow = SW_SHOWNORMAL;
+ LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&seInfo));
+}
diff --git a/src/cascadia/ElevateShim/elevate-shim.rc b/src/cascadia/ElevateShim/elevate-shim.rc
new file mode 100644
index 00000000000..20d7e068e25
--- /dev/null
+++ b/src/cascadia/ElevateShim/elevate-shim.rc
@@ -0,0 +1,69 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico"
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/cascadia/ElevateShim/elevate-shim.vcxproj b/src/cascadia/ElevateShim/elevate-shim.vcxproj
new file mode 100644
index 00000000000..f81353ad368
--- /dev/null
+++ b/src/cascadia/ElevateShim/elevate-shim.vcxproj
@@ -0,0 +1,34 @@
+
+
+
+ {416fd703-baa7-4f6e-9361-64f550ec8fca}
+ Win32Proj
+ elevate-shim
+ elevate-shim
+ elevate-shim
+ Application
+
+
+
+
+
+
+
+
+ NotUsing
+
+
+
+
+
+
+
+
+
+
+
+
+ onecore.lib
+
+
+
diff --git a/src/cascadia/ElevateShim/resource.h b/src/cascadia/ElevateShim/resource.h
new file mode 100644
index 00000000000..62d8839e293
--- /dev/null
+++ b/src/cascadia/ElevateShim/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by wt.rc
+//
+#define IDI_APPICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp
index f2c6fac9d81..a3dcbb45f4e 100644
--- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp
+++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp
@@ -69,6 +69,8 @@ namespace TerminalAppLocalTests
TEST_METHOD(TestIterableColorSchemeCommands);
+ TEST_METHOD(TestElevateArg);
+
TEST_CLASS_SETUP(ClassSetup)
{
return true;
@@ -1267,4 +1269,264 @@ namespace TerminalAppLocalTests
}
}
+ void SettingsTests::TestElevateArg()
+ {
+ static constexpr std::wstring_view settingsJson{ LR"(
+ {
+ "defaultProfile": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
+ "profiles": [
+ {
+ "name": "profile0",
+ "guid": "{6239a42c-0000-49a3-80bd-e8fdd045185c}",
+ "commandline": "cmd.exe"
+ },
+ {
+ "name": "profile1",
+ "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
+ "elevate": true,
+ "commandline": "pwsh.exe"
+ },
+ {
+ "name": "profile2",
+ "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
+ "elevate": false,
+ "commandline": "wsl.exe"
+ }
+ ],
+ "keybindings": [
+ { "keys": ["ctrl+a"], "command": { "action": "newTab", "profile": "profile0" } },
+ { "keys": ["ctrl+b"], "command": { "action": "newTab", "profile": "profile1" } },
+ { "keys": ["ctrl+c"], "command": { "action": "newTab", "profile": "profile2" } },
+
+ { "keys": ["ctrl+d"], "command": { "action": "newTab", "profile": "profile0", "elevate": false } },
+ { "keys": ["ctrl+e"], "command": { "action": "newTab", "profile": "profile1", "elevate": false } },
+ { "keys": ["ctrl+f"], "command": { "action": "newTab", "profile": "profile2", "elevate": false } },
+
+ { "keys": ["ctrl+g"], "command": { "action": "newTab", "profile": "profile0", "elevate": true } },
+ { "keys": ["ctrl+h"], "command": { "action": "newTab", "profile": "profile1", "elevate": true } },
+ { "keys": ["ctrl+i"], "command": { "action": "newTab", "profile": "profile2", "elevate": true } },
+ ]
+ })" };
+
+ const winrt::guid guid0{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-0000-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid1{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-1111-49a3-80bd-e8fdd045185c}") };
+ const winrt::guid guid2{ ::Microsoft::Console::Utils::GuidFromString(L"{6239a42c-2222-49a3-80bd-e8fdd045185c}") };
+
+ CascadiaSettings settings{ settingsJson, {} };
+
+ auto keymap = settings.GlobalSettings().ActionMap();
+ VERIFY_ARE_EQUAL(3u, settings.ActiveProfiles().Size());
+
+ const auto profile2Guid = settings.ActiveProfiles().GetAt(2).Guid();
+ VERIFY_ARE_NOT_EQUAL(winrt::guid{}, profile2Guid);
+
+ VERIFY_ARE_EQUAL(9u, keymap.KeyBindings().Size());
+
+ {
+ Log::Comment(L"profile.elevate=omitted, action.elevate=nullopt: don't auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('A'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NULL(realArgs.TerminalArgs().Elevate());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(false, termSettings.Elevate());
+ }
+ {
+ Log::Comment(L"profile.elevate=true, action.elevate=nullopt: DO auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('B'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NULL(realArgs.TerminalArgs().Elevate());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(true, termSettings.Elevate());
+ }
+ {
+ Log::Comment(L"profile.elevate=false, action.elevate=nullopt: don't auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('C'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NULL(realArgs.TerminalArgs().Elevate());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(false, termSettings.Elevate());
+ }
+
+ {
+ Log::Comment(L"profile.elevate=omitted, action.elevate=false: don't auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('D'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Elevate().Value());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(false, termSettings.Elevate());
+ }
+ {
+ Log::Comment(L"profile.elevate=true, action.elevate=false: don't auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('E'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Elevate().Value());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(false, termSettings.Elevate());
+ }
+ {
+ Log::Comment(L"profile.elevate=false, action.elevate=false: don't auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('F'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Elevate().Value());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(false, termSettings.Elevate());
+ }
+
+ {
+ Log::Comment(L"profile.elevate=omitted, action.elevate=true: DO auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('G'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile0", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Elevate().Value());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"cmd.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(true, termSettings.Elevate());
+ }
+ {
+ Log::Comment(L"profile.elevate=true, action.elevate=true: DO auto elevate");
+ KeyChord kc{ true, false, false, false, static_cast('H'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile1", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Elevate().Value());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"pwsh.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(true, termSettings.Elevate());
+ }
+ {
+ Log::Comment(L"profile.elevate=false, action.elevate=true: DO auto elevate");
+
+ KeyChord kc{ true, false, false, false, static_cast('I'), 0 };
+ auto actionAndArgs = TestUtils::GetActionAndArgs(keymap, kc);
+ VERIFY_ARE_EQUAL(ShortcutAction::NewTab, actionAndArgs.Action());
+ const auto& realArgs = actionAndArgs.Args().try_as();
+ VERIFY_IS_NOT_NULL(realArgs);
+ // Verify the args have the expected value
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Commandline().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().StartingDirectory().empty());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().TabTitle().empty());
+ VERIFY_IS_FALSE(realArgs.TerminalArgs().Profile().empty());
+ VERIFY_ARE_EQUAL(L"profile2", realArgs.TerminalArgs().Profile());
+ VERIFY_IS_NOT_NULL(realArgs.TerminalArgs().Elevate());
+ VERIFY_IS_TRUE(realArgs.TerminalArgs().Elevate().Value());
+
+ const auto termSettingsResult = TerminalSettings::CreateWithNewTerminalArgs(settings, realArgs.TerminalArgs(), nullptr);
+ const auto termSettings = termSettingsResult.DefaultSettings();
+ VERIFY_ARE_EQUAL(L"wsl.exe", termSettings.Commandline());
+ VERIFY_ARE_EQUAL(true, termSettings.Elevate());
+ }
+ }
+
}
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index d5b4fe27598..054e6997497 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -69,6 +69,17 @@ namespace winrt::TerminalApp::implementation
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
+ // Try to handle auto-elevation
+ if (_maybeElevate(newTerminalArgs, settings, profile))
+ {
+ return S_OK;
+ }
+ // We can't go in the other direction (elevated->unelevated)
+ // unfortunately. This seems to be due to Centennial quirks. It works
+ // unpackaged, but not packaged.
+ //
+ // This call to _MakePane won't return nullptr, we already checked that
+ // case above with the _maybeElevate call.
_CreateNewTabFromPane(_MakePane(newTerminalArgs, false, existingConnection));
const uint32_t tabCount = _tabs.Size();
@@ -241,8 +252,11 @@ namespace winrt::TerminalApp::implementation
// - pane: The pane to use as the root.
void TerminalPage::_CreateNewTabFromPane(std::shared_ptr pane)
{
- auto newTabImpl = winrt::make_self(pane);
- _InitializeTab(newTabImpl);
+ if (pane)
+ {
+ auto newTabImpl = winrt::make_self(pane);
+ _InitializeTab(newTabImpl);
+ }
}
// Method Description:
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 70cd3497a15..1ff40f4b25c 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -12,6 +12,8 @@
#include "TerminalPage.g.cpp"
#include
+#include "../WinRTUtils/inc/WtExeUtils.h"
+
#include "TabRowControl.h"
#include "ColorHelper.h"
#include "DebugTapConnection.h"
@@ -549,7 +551,24 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_CompleteInitialization()
{
_startupState = StartupState::Initialized;
- _InitializedHandlers(*this, nullptr);
+
+ // GH#632 - It's possible that the user tried to create the terminal
+ // with only one tab, with only an elevated profile. If that happens,
+ // we'll create _another_ process to host the elevated version of that
+ // profile. This can happen from the jumplist, or if the default profile
+ // is `elevate:true`, or from the commandline.
+ //
+ // However, we need to make sure to close this window in that scenario.
+ // Since there aren't any _tabs_ in this window, we won't ever get a
+ // closed event. So do it manually.
+ if (_tabs.Size() == 0)
+ {
+ _LastTabClosedHandlers(*this, nullptr);
+ }
+ else
+ {
+ _InitializedHandlers(*this, nullptr);
+ }
}
// Method Description:
@@ -865,6 +884,13 @@ namespace winrt::TerminalApp::implementation
else
{
const auto newPane = _MakePane(newTerminalArgs);
+ // If the newTerminalArgs caused us to open an elevated window
+ // instead of creating a pane, it may have returned nullptr. Just do
+ // nothing then.
+ if (!newPane)
+ {
+ return;
+ }
if (altPressed && !debugTap)
{
this->_SplitPane(SplitDirection::Automatic,
@@ -1754,6 +1780,41 @@ namespace winrt::TerminalApp::implementation
}
}
+ // Method Description:
+ // - If the requested settings want us to elevate this new terminal
+ // instance, and we're not currently elevated, then open the new terminal
+ // as an elevated instance (using _OpenElevatedWT). Does nothing if we're
+ // already elevated, or if the control settings don't want to be elevated.
+ // Arguments:
+ // - newTerminalArgs: The NewTerminalArgs for this terminal instance
+ // - controlSettings: The constructed TerminalSettingsCreateResult for this Terminal instance
+ // - profile: The Profile we're using to launch this Terminal instance
+ // Return Value:
+ // - true iff we tossed this request to an elevated window. Callers can use
+ // this result to early-return if needed.
+ bool TerminalPage::_maybeElevate(const NewTerminalArgs& newTerminalArgs,
+ const TerminalSettingsCreateResult& controlSettings,
+ const Profile& profile)
+ {
+ // Try to handle auto-elevation
+ const bool requestedElevation = controlSettings.DefaultSettings().Elevate();
+ const bool currentlyElevated = IsElevated();
+
+ // We aren't elevated, but we want to be.
+ if (requestedElevation && !currentlyElevated)
+ {
+ // Manually set the Profile of the NewTerminalArgs to the guid we've
+ // resolved to. If there was a profile in the NewTerminalArgs, this
+ // will be that profile's GUID. If there wasn't, then we'll use
+ // whatever the default profile's GUID is.
+
+ newTerminalArgs.Profile(::Microsoft::Console::Utils::GuidToString(profile.Guid()));
+ _OpenElevatedWT(newTerminalArgs);
+ return true;
+ }
+ return false;
+ }
+
// Method Description:
// - Split the focused pane either horizontally or vertically, and place the
// given pane accordingly in the tree
@@ -1767,13 +1828,6 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr newPane)
{
const auto focusedTab{ _GetFocusedTabImpl() };
-
- // Do nothing if no TerminalTab is focused
- if (!focusedTab)
- {
- return;
- }
-
_SplitPane(*focusedTab, splitDirection, splitSize, newPane);
}
@@ -1791,6 +1845,14 @@ namespace winrt::TerminalApp::implementation
const float splitSize,
std::shared_ptr newPane)
{
+ // If the caller is calling us with the return value of _MakePane
+ // directly, it's possible that nullptr was returned, if the connections
+ // was supposed to be launched in an elevated window. In that case, do
+ // nothing here. We don't have a pane with which to create the split.
+ if (!newPane)
+ {
+ return;
+ }
const float contentWidth = ::base::saturated_cast(_tabContent.ActualWidth());
const float contentHeight = ::base::saturated_cast(_tabContent.ActualHeight());
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
@@ -2416,7 +2478,13 @@ namespace winrt::TerminalApp::implementation
// a duplicate of the currently focused pane
// - existingConnection: optionally receives a connection from the outside
// world instead of attempting to create one
- std::shared_ptr TerminalPage::_MakePane(const NewTerminalArgs& newTerminalArgs, const bool duplicate, TerminalConnection::ITerminalConnection existingConnection)
+ // Return Value:
+ // - If the newTerminalArgs required us to open the pane as a new elevated
+ // connection, then we'll return nullptr. Otherwise, we'll return a new
+ // Pane for this connection.
+ std::shared_ptr TerminalPage::_MakePane(const NewTerminalArgs& newTerminalArgs,
+ const bool duplicate,
+ TerminalConnection::ITerminalConnection existingConnection)
{
TerminalSettingsCreateResult controlSettings{ nullptr };
Profile profile{ nullptr };
@@ -2447,6 +2515,12 @@ namespace winrt::TerminalApp::implementation
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
}
+ // Try to handle auto-elevation
+ if (_maybeElevate(newTerminalArgs, controlSettings, profile))
+ {
+ return nullptr;
+ }
+
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
if (existingConnection)
{
@@ -3630,6 +3704,63 @@ namespace winrt::TerminalApp::implementation
return profile;
}
+ // Function Description:
+ // - Helper to launch a new WT instance elevated. It'll do this by spawning
+ // a helper process, who will asking the shell to elevate the process for
+ // us. This might cause a UAC prompt. The elevation is performed on a
+ // background thread, as to not block the UI thread.
+ // Arguments:
+ // - newTerminalArgs: A NewTerminalArgs describing the terminal instance
+ // that should be spawned. The Profile should be filled in with the GUID
+ // of the profile we want to launch.
+ // Return Value:
+ // -
+ // Important: Don't take the param by reference, since we'll be doing work
+ // on another thread.
+ void TerminalPage::_OpenElevatedWT(NewTerminalArgs newTerminalArgs)
+ {
+ // BODGY
+ //
+ // We're going to construct the commandline we want, then toss it to a
+ // helper process called `elevate-shim.exe` that happens to live next to
+ // us. elevate-shim.exe will be the one to call ShellExecute with the
+ // args that we want (to elevate the given profile).
+ //
+ // We can't be the one to call ShellExecute ourselves. ShellExecute
+ // requires that the calling process stays alive until the child is
+ // spawned. However, in the case of something like `wt -p
+ // AlwaysElevateMe`, then the original WT will try to ShellExecute a new
+ // wt.exe (elevated) and immediately exit, preventing ShellExecute from
+ // successfully spawning the elevated WT.
+
+ std::filesystem::path exePath = wil::GetModuleFileNameW(nullptr);
+ exePath.replace_filename(L"elevate-shim.exe");
+
+ // Build the commandline to pass to wt for this set of NewTerminalArgs
+ std::wstring cmdline{
+ fmt::format(L"new-tab {}", newTerminalArgs.ToCommandline().c_str())
+ };
+
+ wil::unique_process_information pi;
+ STARTUPINFOW si{};
+ si.cb = sizeof(si);
+
+ LOG_IF_WIN32_BOOL_FALSE(CreateProcessW(exePath.c_str(),
+ cmdline.data(),
+ nullptr,
+ nullptr,
+ FALSE,
+ 0,
+ nullptr,
+ nullptr,
+ &si,
+ &pi));
+
+ // TODO: GH#8592 - It may be useful to pop a Toast here in the original
+ // Terminal window informing the user that the tab was opened in a new
+ // window.
+ }
+
// Method Description:
// - Handles the change of connection state.
// If the connection state is failure show information bar suggesting to configure termination behavior
@@ -3765,4 +3896,5 @@ namespace winrt::TerminalApp::implementation
applicationState.DismissedMessages(std::move(messages));
}
+
}
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index 2d3efb9c025..9ca03d20221 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -404,6 +404,11 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::Settings::Model::Profile GetClosestProfileForDuplicationOfProfile(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile) const noexcept;
+ bool _maybeElevate(const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs,
+ const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& controlSettings,
+ const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
+ void _OpenElevatedWT(winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
+
bool _shouldPromptForCommandline(const winrt::hstring& cmdline) const;
void _adminWarningPrimaryClicked(const winrt::TerminalApp::AdminWarningPlaceholder& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h
index 1c95209e43e..b797dd4d72a 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.h
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h
@@ -110,6 +110,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
ACTION_ARG(winrt::hstring, Profile, L"");
ACTION_ARG(Windows::Foundation::IReference, SuppressApplicationTitle, nullptr);
ACTION_ARG(winrt::hstring, ColorScheme);
+ ACTION_ARG(Windows::Foundation::IReference, Elevate, nullptr);
static constexpr std::string_view CommandlineKey{ "commandline" };
static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" };
@@ -119,6 +120,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static constexpr std::string_view ProfileKey{ "profile" };
static constexpr std::string_view SuppressApplicationTitleKey{ "suppressApplicationTitle" };
static constexpr std::string_view ColorSchemeKey{ "colorScheme" };
+ static constexpr std::string_view ElevateKey{ "elevate" };
public:
hstring GenerateName() const;
@@ -136,6 +138,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
otherAsUs->_ProfileIndex == _ProfileIndex &&
otherAsUs->_Profile == _Profile &&
otherAsUs->_SuppressApplicationTitle == _SuppressApplicationTitle &&
+ otherAsUs->_Elevate == _Elevate &&
otherAsUs->_ColorScheme == _ColorScheme;
}
return false;
@@ -152,6 +155,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
JsonUtils::GetValueForKey(json, TabColorKey, args->_TabColor);
JsonUtils::GetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
JsonUtils::GetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
+ JsonUtils::GetValueForKey(json, ElevateKey, args->_Elevate);
return *args;
}
static Json::Value ToJson(const Model::NewTerminalArgs& val)
@@ -170,6 +174,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
JsonUtils::SetValueForKey(json, TabColorKey, args->_TabColor);
JsonUtils::SetValueForKey(json, SuppressApplicationTitleKey, args->_SuppressApplicationTitle);
JsonUtils::SetValueForKey(json, ColorSchemeKey, args->_ColorScheme);
+ JsonUtils::SetValueForKey(json, ElevateKey, args->_Elevate);
return json;
}
Model::NewTerminalArgs Copy() const
@@ -183,11 +188,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
copy->_Profile = _Profile;
copy->_SuppressApplicationTitle = _SuppressApplicationTitle;
copy->_ColorScheme = _ColorScheme;
+ copy->_Elevate = _Elevate;
return *copy;
}
size_t Hash() const
{
- return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Commandline(), StartingDirectory(), TabTitle(), TabColor(), ProfileIndex(), Profile(), SuppressApplicationTitle(), ColorScheme());
+ return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Commandline(),
+ StartingDirectory(),
+ TabTitle(),
+ TabColor(),
+ ProfileIndex(),
+ Profile(),
+ SuppressApplicationTitle(),
+ ColorScheme(),
+ Elevate());
}
};
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
index 457feefced7..8506c3354e4 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
@@ -117,13 +117,20 @@ namespace Microsoft.Terminal.Settings.Model
String TabTitle;
Windows.Foundation.IReference TabColor;
String Profile; // Either a GUID or a profile's name if the GUID isn't a match
+
+ // We use IReference<> to treat some args as nullable where null means
+ // "use the inherited value". See ProfileIndex,
+ // SuppressApplicationTitle, Elevate. Strings that behave this way just
+ // use `null` as "use the inherited value".
+
// ProfileIndex can be null (for "use the default"), so this needs to be
// a IReference, so it's nullable
Windows.Foundation.IReference ProfileIndex { get; };
-
Windows.Foundation.IReference SuppressApplicationTitle;
-
String ColorScheme;
+ // This needs to be an optional so that the default value (null) does
+ // not modify whatever the profile's value is (either true or false)
+ Windows.Foundation.IReference Elevate { get; };
Boolean Equals(NewTerminalArgs other);
String GenerateName();
diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
index 6719818e745..c29977a5e15 100644
--- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h
+++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h
@@ -73,7 +73,8 @@ Author(s):
X(hstring, Icon, "icon", L"\uE756") \
X(CloseOnExitMode, CloseOnExit, "closeOnExit", CloseOnExitMode::Graceful) \
X(hstring, TabTitle, "tabTitle") \
- X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible)
+ X(Model::BellStyle, BellStyle, "bellStyle", BellStyle::Audible) \
+ X(bool, Elevate, "elevate", false)
#define MTSM_FONT_SETTINGS(X) \
X(hstring, FontFace, "face", DEFAULT_FONT_FACE) \
diff --git a/src/cascadia/TerminalSettingsModel/Profile.cpp b/src/cascadia/TerminalSettingsModel/Profile.cpp
index f56e99d4afe..912e6bc3380 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.cpp
+++ b/src/cascadia/TerminalSettingsModel/Profile.cpp
@@ -30,6 +30,7 @@ static constexpr std::string_view FontInfoKey{ "font" };
static constexpr std::string_view PaddingKey{ "padding" };
static constexpr std::string_view TabColorKey{ "tabColor" };
static constexpr std::string_view UnfocusedAppearanceKey{ "unfocusedAppearance" };
+static constexpr std::string_view ElevateKey{ "elevate" };
Profile::Profile(guid guid) noexcept :
_Guid(guid)
@@ -333,5 +334,7 @@ Json::Value Profile::ToJson() const
json[JsonKey(UnfocusedAppearanceKey)] = winrt::get_self(_UnfocusedAppearance.value())->ToJson();
}
+ JsonUtils::SetValueForKey(json, ElevateKey, _Elevate);
+
return json;
}
diff --git a/src/cascadia/TerminalSettingsModel/Profile.idl b/src/cascadia/TerminalSettingsModel/Profile.idl
index 746caad252b..810448b0397 100644
--- a/src/cascadia/TerminalSettingsModel/Profile.idl
+++ b/src/cascadia/TerminalSettingsModel/Profile.idl
@@ -79,5 +79,6 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(Boolean, SnapOnInput);
INHERITABLE_PROFILE_SETTING(Boolean, AltGrAliasing);
INHERITABLE_PROFILE_SETTING(BellStyle, BellStyle);
+ INHERITABLE_PROFILE_SETTING(Boolean, Elevate);
}
}
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
index ae995398641..5bc300f0b4d 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
@@ -155,6 +155,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
defaultSettings.ApplyColorScheme(scheme);
}
}
+ // Elevate on NewTerminalArgs is an optional value, so the default
+ // value (null) doesn't override a profile's value. Note that
+ // elevate:false in an already elevated terminal does nothing - the
+ // profile will still be launched elevated.
+ if (newTerminalArgs.Elevate())
+ {
+ defaultSettings.Elevate(newTerminalArgs.Elevate().Value());
+ }
}
return settingsPair;
@@ -307,6 +315,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
const til::color colorRef{ profile.TabColor().Value() };
_TabColor = static_cast(colorRef);
}
+
+ _Elevate = profile.Elevate();
}
// Method Description:
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h
index 096f18f7988..0baf0b7158f 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h
@@ -159,6 +159,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderPath);
INHERITABLE_SETTING(Model::TerminalSettings, bool, IntenseIsBold);
+ INHERITABLE_SETTING(Model::TerminalSettings, bool, Elevate, false);
+
private:
std::optional> _ColorTable;
gsl::span _getColorTableImpl();
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl
index 80231906e8f..52620a5eb65 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettings.idl
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.idl
@@ -35,5 +35,7 @@ namespace Microsoft.Terminal.Settings.Model
void ApplyColorScheme(ColorScheme scheme);
ColorScheme AppliedColorScheme;
+
+ Boolean Elevate;
};
}