Compare commits

..

13 Commits

Author SHA1 Message Date
Dustin L. Howett
eb72c7fd3b Migrate spelling-0.0.21 changes from main 2020-11-13 13:16:30 -08:00
Dustin L. Howett
20250c233d Migrate spelling-0.0.19 changes from main 2020-11-13 13:16:30 -08:00
Michael Niksa
39023426ff safety check on alpha test. 2020-11-13 13:16:30 -08:00
Michael Niksa
9ea43bace0 corrupt the alphas for v1 only 2020-11-13 13:15:13 -08:00
Michael Niksa
2b9f6388cf Add theoretical v2 tests too. 2020-11-13 13:12:28 -08:00
Michael Niksa
c340ab5e7b holy v1 tests, batman 2020-11-13 12:00:41 -08:00
Michael Niksa
42e198e967 so much pain. 2020-11-13 11:40:45 -08:00
Michael Niksa
a6816df2b5 add these miserable tests for direct read. 2020-11-11 16:50:52 -08:00
Michael Niksa
3aee3992ba update notes and update read function 2020-11-05 16:41:23 -08:00
Michael Niksa
1b3a5e671b Two more tests that make me sad about chv1 2020-11-05 16:17:01 -08:00
Michael Niksa
18b91c20ef Add some more tests to discover behaviors. 2020-11-05 15:59:53 -08:00
Michael Niksa
3b7544770b Make the font be a differentiator in the test. 2020-11-04 10:36:05 -08:00
Michael Niksa
93b95420ac Alias tests. 2020-11-04 10:36:05 -08:00
75 changed files with 1592 additions and 2698 deletions

View File

@@ -1,12 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Microsoft Security Response Center 🔐
url: https://msrc.microsoft.com/create-report
about: Please report security vulnerabilities here.
- name: Windows Terminal Documentation issue 📄
url: https://github.com/MicrosoftDocs/terminal/issues/new
about: Report issues with the documentation for the Windows Terminal (in docs.microsoft.com/windows/terminal)
- name: Console Documentation issue 📄
url: https://github.com/MicrosoftDocs/console-docs/issues/new
about: Report issues with the documentation for the Console (in docs.microsoft.com/windows/console)

57
.github/workflows/linter.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
---
###########################
###########################
## Linter GitHub Actions ##
###########################
###########################
name: Lint Code Base
#
# Documentation:
# https://help.github.com/en/articles/workflow-syntax-for-github-actions
#
###################################################
# The linter is noisy; we used to run it on push. #
###################################################
#
#on:
# pull_request:
# branches: [main]
###############
# Set the Job #
###############
jobs:
build:
# Name the Job
name: Lint Code Base
# Set the agent to run on
runs-on: ubuntu-latest
##################
# Load all steps #
##################
steps:
##########################
# Checkout the code base #
##########################
- name: Checkout Code
uses: actions/checkout@v2
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
################################
# Run Linter against code base #
################################
- name: Lint Code Base
uses: github/super-linter@v3
env:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
MARKDOWN_CONFIG_FILE: .markdown-lint.yml
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_EDITORCONFIG: false
# The json linter doesn't like JSONC, which we use all over. So just disable it.
VALIDATE_JSON: false

View File

@@ -313,24 +313,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfTerminalTestNetCore", "s
{84848BFA-931D-42CE-9ADF-01EE54DE7890} = {84848BFA-931D-42CE-9ADF-01EE54DE7890}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchWinRTServer", "src\tools\ScratchWinRTServer\ScratchWinRTServer.vcxproj", "{D46D9547-F085-4645-B8F7-E8CD21559AB4}"
ProjectSection(ProjectDependencies) = postProject
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B} = {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
{CA5CAD1A-ABCD-429C-B551-8562EC954746} = {CA5CAD1A-ABCD-429C-B551-8562EC954746}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}
{48D21369-3D7B-4431-9967-24E81292CF62} = {48D21369-3D7B-4431-9967-24E81292CF62}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchWinRTClient", "src\tools\ScratchWinRTClient\ScratchWinRTClient.vcxproj", "{06382349-D62A-4C7D-A7D3-9CA817EAE092}"
ProjectSection(ProjectDependencies) = postProject
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchIsland", "src\tools\ScratchIsland\ScratchIsland.vcxproj", "{23A1F736-CD19-4196-980F-84BCD50CF783}"
ProjectSection(ProjectDependencies) = postProject
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4}
EndProjectSection
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}") = "Microsoft.Terminal.Settings.Model.Lib", "src\cascadia\TerminalSettingsModel\Microsoft.Terminal.Settings.ModelLib.vcxproj", "{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}"
@@ -2003,90 +1985,6 @@ Global
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|DotNet_x86Test.Build.0 = Release|x86
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x64.ActiveCfg = Release|Any CPU
{1588FD7C-241E-4E7D-9113-43735F3E6BAD}.Release|x86.ActiveCfg = Release|Any CPU
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|Any CPU.ActiveCfg = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|Any CPU.Build.0 = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|ARM64.ActiveCfg = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|ARM64.Build.0 = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x64.ActiveCfg = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x64.Build.0 = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x86.ActiveCfg = Release|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.AuditMode|x86.Build.0 = Release|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|Any CPU.ActiveCfg = Debug|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|ARM64.ActiveCfg = Debug|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x64.ActiveCfg = Debug|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x64.Build.0 = Debug|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x86.ActiveCfg = Debug|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Debug|x86.Build.0 = Debug|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|Any CPU.ActiveCfg = Release|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|ARM64.ActiveCfg = Release|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x64.ActiveCfg = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x64.Build.0 = Release|x64
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x86.ActiveCfg = Release|Win32
{D46D9547-F085-4645-B8F7-E8CD21559AB4}.Release|x86.Build.0 = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|Any CPU.ActiveCfg = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|Any CPU.Build.0 = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|ARM64.ActiveCfg = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|ARM64.Build.0 = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x64.ActiveCfg = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x64.Build.0 = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x86.ActiveCfg = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.AuditMode|x86.Build.0 = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|Any CPU.ActiveCfg = Debug|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|ARM64.ActiveCfg = Debug|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x64.ActiveCfg = Debug|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x64.Build.0 = Debug|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x86.ActiveCfg = Debug|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Debug|x86.Build.0 = Debug|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|Any CPU.ActiveCfg = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|ARM64.ActiveCfg = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x64.ActiveCfg = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x64.Build.0 = Release|x64
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x86.ActiveCfg = Release|Win32
{06382349-D62A-4C7D-A7D3-9CA817EAE092}.Release|x86.Build.0 = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|Any CPU.ActiveCfg = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|Any CPU.Build.0 = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|ARM64.ActiveCfg = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|ARM64.Build.0 = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x64.ActiveCfg = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x64.Build.0 = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x86.ActiveCfg = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.AuditMode|x86.Build.0 = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|Any CPU.ActiveCfg = Debug|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|ARM64.ActiveCfg = Debug|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x64.ActiveCfg = Debug|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x64.Build.0 = Debug|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x86.ActiveCfg = Debug|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Debug|x86.Build.0 = Debug|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|Any CPU.ActiveCfg = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|ARM64.ActiveCfg = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x64.ActiveCfg = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x64.Build.0 = Release|x64
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x86.ActiveCfg = Release|Win32
{23A1F736-CD19-4196-980F-84BCD50CF783}.Release|x86.Build.0 = Release|Win32
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
@@ -2271,9 +2169,6 @@ Global
{067F0A06-FCB7-472C-96E9-B03B54E8E18D} = {59840756-302F-44DF-AA47-441A9D673202}
{6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {81C352DB-1818-45B7-A284-18E259F1CC87}
{1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202}
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {A10C4720-DCA4-4640-9749-67F4314F527C}
{06382349-D62A-4C7D-A7D3-9CA817EAE092} = {A10C4720-DCA4-4640-9749-67F4314F527C}
{23A1F736-CD19-4196-980F-84BCD50CF783} = {A10C4720-DCA4-4640-9749-67F4314F527C}
{506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {59840756-302F-44DF-AA47-441A9D673202}
{CA5CAD1A-082C-4476-9F33-94B339494076} = {59840756-302F-44DF-AA47-441A9D673202}

View File

@@ -685,38 +685,8 @@
},
"useTabSwitcher": {
"default": true,
"description": "Deprecated. Please use \"tabSwitcherMode\" instead.",
"oneOf": [
{
"type": "boolean"
},
{
"enum": [
"mru",
"inOrder",
"disabled",
],
"type": "string"
}
],
"deprecated": true
},
"tabSwitcherMode": {
"default": true,
"description": "When set to \"true\" or \"mru\", the \"nextTab\" and \"prevTab\" commands will use the tab switcher UI, with most-recently-used ordering. When set to \"inOrder\", these actions will switch tabs in their current ordering. Set to \"false\" to disable the tab switcher.",
"oneOf": [
{
"type": "boolean"
},
{
"enum": [
"mru",
"inOrder",
"disabled",
],
"type": "string"
}
]
"description": "When set to \"true\", the \"nextTab\" and \"prevTab\" commands will use the tab switcher UI.",
"type": "boolean"
}
},
"required": [

View File

@@ -83,8 +83,6 @@ namespace SettingsModelLocalTests
TEST_METHOD(TestCopy);
TEST_METHOD(TestCloneInheritanceTree);
TEST_METHOD(TestValidDefaults);
TEST_CLASS_SETUP(ClassSetup)
{
InitializeJsonReader();
@@ -2585,13 +2583,4 @@ namespace SettingsModelLocalTests
verifyEmptyPD(emptyPDJson);
verifyEmptyPD(missingPDJson);
}
void DeserializationTests::TestValidDefaults()
{
// GH#8146: A LoadDefaults call should populate the list of active profiles
const auto settings{ CascadiaSettings::LoadDefaults() };
VERIFY_ARE_EQUAL(settings.ActiveProfiles().Size(), settings.AllProfiles().Size());
VERIFY_ARE_EQUAL(settings.AllProfiles().Size(), 2u);
}
}

View File

@@ -7,32 +7,17 @@
#include "../TerminalApp/MinMaxCloseControl.h"
#include "../TerminalApp/TabRowControl.h"
#include "../TerminalApp/ShortcutActionDispatch.h"
#include "../TerminalApp/TerminalTab.h"
#include "../TerminalApp/Tab.h"
#include "../CppWinrtTailored.h"
using namespace Microsoft::Console;
using namespace TerminalApp;
using namespace winrt::TerminalApp;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace WEX::Common;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::System;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::Text;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
using IInspectable = Windows::Foundation::IInspectable;
}
namespace TerminalAppLocalTests
{
@@ -81,8 +66,6 @@ namespace TerminalAppLocalTests
TEST_METHOD(MoveFocusFromZoomedPane);
TEST_METHOD(CloseZoomedPane);
TEST_METHOD(NextMRUTab);
TEST_CLASS_SETUP(ClassSetup)
{
return true;
@@ -99,13 +82,6 @@ namespace TerminalAppLocalTests
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> _commonSetup();
};
template<typename TFunction>
void TestOnUIThread(const TFunction& function)
{
const auto result = RunOnUIThread(function);
VERIFY_SUCCEEDED(result);
}
void TabTests::EnsureTestsActivate()
{
// This test was originally used to ensure that XAML Islands was
@@ -274,8 +250,8 @@ namespace TerminalAppLocalTests
// In the real app, this isn't a problem, but doesn't happen
// reliably in the unit tests.
Log::Comment(L"Ensure we set the first tab as the selected one.");
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
page->_tabView.SelectedItem(tab->TabViewItem());
auto tab{ page->_GetStrongTabImpl(0) };
page->_tabView.SelectedItem(tab->GetTabViewItem());
page->_UpdatedSelectedTab(0);
});
VERIFY_SUCCEEDED(result);
@@ -477,7 +453,7 @@ namespace TerminalAppLocalTests
result = RunOnUIThread([&page]() {
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
auto tab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(1, tab->GetLeafPaneCount());
});
VERIFY_SUCCEEDED(result);
@@ -487,7 +463,7 @@ namespace TerminalAppLocalTests
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
auto tab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, tab->GetLeafPaneCount());
});
VERIFY_SUCCEEDED(result);
@@ -505,7 +481,7 @@ namespace TerminalAppLocalTests
page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr);
VERIFY_ARE_EQUAL(1u, page->_tabs.Size());
auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0));
auto tab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2,
tab->GetLeafPaneCount(),
L"We should gracefully do nothing here - the profile no longer exists.");
@@ -537,31 +513,16 @@ namespace TerminalAppLocalTests
const std::string settingsJson0{ R"(
{
"defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"showTabsInTitlebar": false,
"profiles": [
{
"name" : "profile0",
"guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}",
"tabTitle" : "Profile 0",
"historySize": 1
},
{
"name" : "profile1",
"guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}",
"tabTitle" : "Profile 1",
"historySize": 2
},
{
"name" : "profile2",
"guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}",
"tabTitle" : "Profile 2",
"historySize": 3
},
{
"name" : "profile3",
"guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}",
"tabTitle" : "Profile 3",
"historySize": 4
}
]
})" };
@@ -601,7 +562,7 @@ namespace TerminalAppLocalTests
ActionEventArgs eventArgs{ args };
// eventArgs.Args(args);
page->_HandleSplitPane(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_FALSE(firstTab->IsZoomed());
@@ -612,7 +573,7 @@ namespace TerminalAppLocalTests
result = RunOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleTogglePaneZoom(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_TRUE(firstTab->IsZoomed());
});
@@ -622,7 +583,7 @@ namespace TerminalAppLocalTests
result = RunOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleTogglePaneZoom(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_FALSE(firstTab->IsZoomed());
});
@@ -639,7 +600,7 @@ namespace TerminalAppLocalTests
SplitPaneArgs args{ SplitType::Duplicate };
ActionEventArgs eventArgs{ args };
page->_HandleSplitPane(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_FALSE(firstTab->IsZoomed());
@@ -653,7 +614,7 @@ namespace TerminalAppLocalTests
page->_HandleTogglePaneZoom(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_TRUE(firstTab->IsZoomed());
});
@@ -667,7 +628,7 @@ namespace TerminalAppLocalTests
page->_HandleMoveFocus(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_FALSE(firstTab->IsZoomed());
});
@@ -684,7 +645,7 @@ namespace TerminalAppLocalTests
SplitPaneArgs args{ SplitType::Duplicate };
ActionEventArgs eventArgs{ args };
page->_HandleSplitPane(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_FALSE(firstTab->IsZoomed());
@@ -698,7 +659,7 @@ namespace TerminalAppLocalTests
page->_HandleTogglePaneZoom(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount());
VERIFY_IS_TRUE(firstTab->IsZoomed());
});
@@ -711,7 +672,7 @@ namespace TerminalAppLocalTests
page->_HandleClosePane(nullptr, eventArgs);
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_IS_FALSE(firstTab->IsZoomed());
});
VERIFY_SUCCEEDED(result);
@@ -722,130 +683,10 @@ namespace TerminalAppLocalTests
Log::Comment(L"Check to ensure there's only one pane left.");
result = RunOnUIThread([&page]() {
auto firstTab = page->_GetTerminalTabImpl(0);
auto firstTab = page->_GetStrongTabImpl(0);
VERIFY_ARE_EQUAL(1, firstTab->GetLeafPaneCount());
VERIFY_IS_FALSE(firstTab->IsZoomed());
});
VERIFY_SUCCEEDED(result);
}
void TabTests::NextMRUTab()
{
// This is a test for GH#8025 - we want to make sure that we can do both
// in-order and MRU tab traversal, using the tab switcher and with the
// tab switcher disabled.
auto page = _commonSetup();
Log::Comment(L"Create a second tab");
TestOnUIThread([&page]() {
NewTerminalArgs newTerminalArgs{ 1 };
page->_OpenNewTab(newTerminalArgs);
});
VERIFY_ARE_EQUAL(2u, page->_tabs.Size());
Log::Comment(L"Create a third tab");
TestOnUIThread([&page]() {
NewTerminalArgs newTerminalArgs{ 2 };
page->_OpenNewTab(newTerminalArgs);
});
VERIFY_ARE_EQUAL(3u, page->_tabs.Size());
Log::Comment(L"Create a fourth tab");
TestOnUIThread([&page]() {
NewTerminalArgs newTerminalArgs{ 3 };
page->_OpenNewTab(newTerminalArgs);
});
VERIFY_ARE_EQUAL(4u, page->_tabs.Size());
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
});
Log::Comment(L"Select the second tab");
TestOnUIThread([&page]() {
page->_SelectTab(1);
});
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
});
Log::Comment(L"Change the tab switch order to MRU switching");
TestOnUIThread([&page]() {
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::MostRecentlyUsed);
});
Log::Comment(L"Switch to the next MRU tab, which is the fourth tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
});
Log::Comment(L"Sleep to let events propagate");
Sleep(250);
TestOnUIThread([&page]() {
Log::Comment(L"Hide the command palette, to confirm the selection");
// If you don't do this, the palette will just stay open, and the
// next time we call _HandleNextTab, we'll continue traversing the
// MRU list, instead of just hoping one entry.
page->CommandPalette().Visibility(Visibility::Collapsed);
});
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
});
Log::Comment(L"Switch to the next MRU tab, which is the second tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
});
Log::Comment(L"Sleep to let events propagate");
Sleep(250);
TestOnUIThread([&page]() {
Log::Comment(L"Hide the command palette, to confirm the selection");
// If you don't do this, the palette will just stay open, and the
// next time we call _HandleNextTab, we'll continue traversing the
// MRU list, instead of just hoping one entry.
page->CommandPalette().Visibility(Visibility::Collapsed);
});
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
VERIFY_ARE_EQUAL(1u, focusedIndex, L"Verify the second tab is the focused one");
});
Log::Comment(L"Change the tab switch order to in-order switching");
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::InOrder);
Log::Comment(L"Switch to the next in-order tab, which is the third tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
});
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
VERIFY_ARE_EQUAL(2u, focusedIndex, L"Verify the third tab is the focused one");
});
Log::Comment(L"Change the tab switch order to not use the tab switcher (which is in-order always)");
page->_settings.GlobalSettings().TabSwitcherMode(TabSwitcherMode::Disabled);
Log::Comment(L"Switch to the next in-order tab, which is the fourth tab");
TestOnUIThread([&page]() {
ActionEventArgs eventArgs{};
page->_HandleNextTab(nullptr, eventArgs);
});
TestOnUIThread([&page]() {
uint32_t focusedIndex = page->_GetFocusedTabIndex().value_or(-1);
VERIFY_ARE_EQUAL(3u, focusedIndex, L"Verify the fourth tab is the focused one");
});
}
}

View File

@@ -130,26 +130,21 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (auto focusedTab = _GetFocusedTab())
auto activeTab = _GetFocusedTab();
// Don't do anything if there's only one pane. It's already zoomed.
if (activeTab && activeTab->GetLeafPaneCount() > 1)
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
// Don't do anything if there's only one pane. It's already zoomed.
if (activeTab && activeTab->GetLeafPaneCount() > 1)
{
// First thing's first, remove the current content from the UI
// tree. This is important, because we might be leaving zoom, and if
// a pane is zoomed, then it's currently in the UI tree, and should
// be removed before it's re-added in Pane::Restore
_tabContent.Children().Clear();
// First thing's first, remove the current content from the UI
// tree. This is important, because we might be leaving zoom, and if
// a pane is zoomed, then it's currently in the UI tree, and should
// be removed before it's re-added in Pane::Restore
_tabContent.Children().Clear();
// Togging the zoom on the tab will cause the tab to inform us of
// the new root Content for this tab.
activeTab->ToggleZoom();
}
}
// Togging the zoom on the tab will cause the tab to inform us of
// the new root Content for this tab.
activeTab->ToggleZoom();
}
args.Handled(true);
}
@@ -328,19 +323,16 @@ namespace winrt::TerminalApp::implementation
args.Handled(false);
if (const auto& realArgs = args.ActionArgs().try_as<SetColorSchemeArgs>())
{
if (auto focusedTab = _GetFocusedTab())
if (auto activeTab = _GetFocusedTab())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
if (auto activeControl = activeTab->GetActiveTerminalControl())
{
if (auto activeControl = activeTab->GetActiveTerminalControl())
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
{
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
{
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
controlSettings->ApplyColorScheme(scheme);
activeControl.UpdateSettings(*controlSettings);
args.Handled(true);
}
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
controlSettings->ApplyColorScheme(scheme);
activeControl.UpdateSettings(*controlSettings);
args.Handled(true);
}
}
}
@@ -360,18 +352,16 @@ namespace winrt::TerminalApp::implementation
}
}
if (auto focusedTab = _GetFocusedTab())
auto activeTab = _GetFocusedTab();
if (activeTab)
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
if (tabColor.has_value())
{
if (tabColor.has_value())
{
activeTab->SetRuntimeTabColor(tabColor.value());
}
else
{
activeTab->ResetRuntimeTabColor();
}
activeTab->SetRuntimeTabColor(tabColor.value());
}
else
{
activeTab->ResetRuntimeTabColor();
}
}
args.Handled(true);
@@ -380,12 +370,10 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (auto focusedTab = _GetFocusedTab())
auto activeTab = _GetFocusedTab();
if (activeTab)
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
activeTab->ActivateColorPicker();
}
activeTab->ActivateColorPicker();
}
args.Handled(true);
}
@@ -400,18 +388,16 @@ namespace winrt::TerminalApp::implementation
title = realArgs.Title();
}
if (auto focusedTab = _GetFocusedTab())
auto activeTab = _GetFocusedTab();
if (activeTab)
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
if (title.has_value())
{
if (title.has_value())
{
activeTab->SetTabText(title.value());
}
else
{
activeTab->ResetTabText();
}
activeTab->SetTabText(title.value());
}
else
{
activeTab->ResetTabText();
}
}
args.Handled(true);
@@ -420,12 +406,10 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleOpenTabRenamer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (auto focusedTab = _GetFocusedTab())
auto activeTab = _GetFocusedTab();
if (activeTab)
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
activeTab->ActivateTabRenamer();
}
activeTab->ActivateTabRenamer();
}
args.Handled(true);
}
@@ -524,7 +508,12 @@ namespace winrt::TerminalApp::implementation
const ActionEventArgs& args)
{
// Tab search is always in-order.
_UpdatePaletteWithInOrderTabs();
auto tabCommands = winrt::single_threaded_vector<Command>();
for (const auto& tab : _tabs)
{
tabCommands.Append(tab.SwitchToTabCommand());
}
CommandPalette().SetTabActions(tabCommands);
auto opt = _GetFocusedTabIndex();
uint32_t startIdx = opt.value_or(0);

View File

@@ -470,11 +470,6 @@ namespace winrt::TerminalApp::implementation
void AppLogic::_OnLoaded(const IInspectable& /*sender*/,
const RoutedEventArgs& /*eventArgs*/)
{
const auto keyboardServiceIsDisabled = !_IsKeyboardServiceEnabled();
if (keyboardServiceIsDisabled)
{
_root->ShowKeyboardServiceWarning();
}
if (FAILED(_settingsLoadedResult))
{
const winrt::hstring titleKey = USES_RESOURCE(L"InitialJsonParseErrorTitle");
@@ -487,51 +482,6 @@ namespace winrt::TerminalApp::implementation
}
}
// Method Description:
// - Helper for determining if the "Touch Keyboard and Handwriting Panel
// Service" is enabled. If it isn't, we want to be able to display a
// warning to the user, because they won't be able to type in the
// Terminal.
// Return Value:
// - true if the service is enabled, or if we fail to query the service. We
// return true in that case, to be less noisy (though, that is unexpected)
bool AppLogic::_IsKeyboardServiceEnabled()
{
if (IsUwp())
{
return true;
}
// If at any point we fail to open the service manager, the service,
// etc, then just quick return true to disable the dialog. We'd rather
// not be noisy with this dialog if we failed for some reason.
// Open the service manager. This will return 0 if it failed.
wil::unique_schandle hManager{ OpenSCManager(nullptr, nullptr, 0) };
if (LOG_LAST_ERROR_IF(!hManager.is_valid()))
{
return true;
}
// Get a handle to the keyboard service
wil::unique_schandle hService{ OpenService(hManager.get(), TabletInputServiceKey.data(), SERVICE_QUERY_STATUS) };
if (LOG_LAST_ERROR_IF(!hService.is_valid()))
{
return true;
}
// Get the current state of the service
SERVICE_STATUS status{ 0 };
if (!LOG_IF_WIN32_BOOL_FALSE(QueryServiceStatus(hService.get(), &status)))
{
return true;
}
const auto state = status.dwCurrentState;
return (state == SERVICE_RUNNING || state == SERVICE_START_PENDING);
}
// Method Description:
// - Get the size in pixels of the client area we'll need to launch this
// terminal app. This method will use the default profile's settings to do

View File

@@ -4,6 +4,8 @@
#pragma once
#include "AppLogic.g.h"
#include "Tab.h"
#include "TerminalPage.h"
#include "Jumplist.h"
#include "../../cascadia/inc/cppwinrt_utils.h"
@@ -84,8 +86,6 @@ namespace winrt::TerminalApp::implementation
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult);
void _ShowLoadWarningsDialog();
bool _IsKeyboardServiceEnabled();
void _ShowKeyboardServiceDisabledDialog();
fire_and_forget _LoadErrorsDialogRoutine();
fire_and_forget _ShowLoadWarningsDialogRoutine();

View File

@@ -28,7 +28,7 @@ namespace winrt::TerminalApp::implementation
_nestedActionStack = winrt::single_threaded_vector<Command>();
_currentNestedCommands = winrt::single_threaded_vector<Command>();
_allCommands = winrt::single_threaded_vector<Command>();
_tabActions = winrt::single_threaded_vector<Command>();
_allTabActions = winrt::single_threaded_vector<Command>();
_switchToMode(CommandPaletteMode::ActionMode);
@@ -51,9 +51,9 @@ namespace winrt::TerminalApp::implementation
if (_currentMode == CommandPaletteMode::TabSwitchMode)
{
_searchBox().Visibility(Visibility::Collapsed);
_filteredActionsView().Focus(FocusState::Keyboard);
_filteredActionsView().SelectedIndex(_switcherStartIdx);
_filteredActionsView().ScrollIntoView(_filteredActionsView().SelectedItem());
_filteredActionsView().Focus(FocusState::Keyboard);
// Do this right after becoming visible so we can quickly catch scenarios where
// modifiers aren't held down (e.g. command palette invocation).
@@ -61,9 +61,9 @@ namespace winrt::TerminalApp::implementation
}
else
{
_filteredActionsView().SelectedIndex(0);
_searchBox().Focus(FocusState::Programmatic);
_updateFilteredActions();
_filteredActionsView().SelectedIndex(0);
}
TraceLoggingWrite(
@@ -428,25 +428,6 @@ namespace winrt::TerminalApp::implementation
_dispatchCommand(e.ClickedItem().try_as<Command>());
}
// Method Description:
// This event is called when the user clicks on an ChevronLeft button right
// next to the ParentCommandName (e.g. New Tab...) above the subcommands list.
// It'll go up a level when the users click the button.
// Arguments:
// - sender: the button that got clicked
// Return Value:
// - <none>
void CommandPalette::_moveBackButtonClicked(Windows::Foundation::IInspectable const& /*sender*/,
Windows::UI::Xaml::RoutedEventArgs const&)
{
_nestedActionStack.Clear();
ParentCommandName(L"");
_currentNestedCommands.Clear();
_searchBox().Focus(FocusState::Programmatic);
_updateFilteredActions();
_filteredActionsView().SelectedIndex(0);
}
// Method Description:
// - This is called when the user selects a command with subcommands. It
// will update our UI to now display the list of subcommands instead, and
@@ -491,9 +472,8 @@ namespace winrt::TerminalApp::implementation
return _allCommands;
case CommandPaletteMode::TabSearchMode:
return _tabActions;
case CommandPaletteMode::TabSwitchMode:
return _tabActions;
return _allTabActions;
case CommandPaletteMode::CommandlineMode:
return winrt::single_threaded_vector<Command>();
default:
@@ -704,20 +684,9 @@ namespace winrt::TerminalApp::implementation
_updateFilteredActions();
}
void CommandPalette::SetTabActions(Collections::IVector<Command> const& tabs, const bool clearList)
void CommandPalette::SetTabActions(Collections::IVector<Command> const& tabs)
{
_tabActions = tabs;
// The smooth remove/add animations that happen during
// UpdateFilteredActions don't work very well with changing the tab
// order, because of the sheer amount of remove/adds. So, let's just
// clear & rebuild the list when we change the set of tabs.
//
// Some callers might actually want smooth updating, like when the list
// of tabs changes.
if (clearList && _currentMode == CommandPaletteMode::TabSwitchMode)
{
_filteredActions.Clear();
}
_allTabActions = tabs;
_updateFilteredActions();
}
@@ -1097,5 +1066,4 @@ namespace winrt::TerminalApp::implementation
_updateFilteredActions();
}
}

View File

@@ -23,7 +23,7 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IObservableVector<Microsoft::Terminal::Settings::Model::Command> FilteredActions();
void SetCommands(Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> const& actions);
void SetTabActions(Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> const& tabs, const bool clearList);
void SetTabActions(Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> const& tabs);
void SetKeyBindings(Microsoft::Terminal::TerminalControl::IKeyBindings bindings);
void EnableCommandPaletteMode();
@@ -40,7 +40,6 @@ namespace winrt::TerminalApp::implementation
// Tab Switcher
void EnableTabSwitcherMode(const bool searchMode, const uint32_t startIdx);
void SetTabSwitchOrder(const Microsoft::Terminal::Settings::Model::TabSwitcherMode order);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
@@ -58,6 +57,7 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _nestedActionStack{ nullptr };
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _commandsToFilter();
bool _lastFilterTextWasEmpty{ true };
@@ -81,8 +81,6 @@ namespace winrt::TerminalApp::implementation
void _listItemClicked(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Controls::ItemClickEventArgs const& e);
void _moveBackButtonClicked(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const&);
void _updateFilteredActions();
std::vector<Microsoft::Terminal::Settings::Model::Command> _collectFilteredActions();
@@ -99,7 +97,7 @@ namespace winrt::TerminalApp::implementation
Microsoft::Terminal::TerminalControl::IKeyBindings _bindings;
// Tab Switcher
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _tabActions{ nullptr };
Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command> _allTabActions{ nullptr };
uint32_t _switcherStartIdx;
void _anchorKeyUpHandler();

View File

@@ -19,7 +19,7 @@ namespace TerminalApp
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Model.Command> FilteredActions { get; };
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
void SetTabActions(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> tabs, Boolean clearList);
void SetTabActions(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> tabs);
void SetKeyBindings(Microsoft.Terminal.TerminalControl.IKeyBindings bindings);
void EnableCommandPaletteMode();

View File

@@ -186,36 +186,16 @@ the MIT License. See LICENSE in the project root for license information. -->
>
</TextBlock>
<StackPanel Orientation="Horizontal"
Padding="16, 0, 16, 4"
Grid.Row="1"
Visibility="{x:Bind ParentCommandName,
Mode=OneWay,
Converter={StaticResource ParentCommandVisibilityConverter}}">
<Button
Background="Transparent"
x:Name="_parentCommandBackButton"
x:Uid="ParentCommandBackButton"
Click="_moveBackButtonClicked"
ClickMode="Press"
VerticalAlignment="Center">
<FontIcon
FontSize="12"
FontFamily="Segoe MDL2 Assets"
Glyph="&#xE76b;">
</FontIcon>
</Button>
<TextBlock
Padding="16, 0, 16, 4"
x:Name="_parentCommandText"
FontStyle="Italic"
Grid.Row="1"
Text="{x:Bind ParentCommandName, Mode=OneWay}"
VerticalAlignment="Center">
</TextBlock>
</StackPanel>
<TextBlock
Padding="16, 0, 16, 4"
x:Name="_parentCommandText"
FontStyle="Italic"
Visibility="{x:Bind ParentCommandName,
Mode=OneWay,
Converter={StaticResource ParentCommandVisibilityConverter}}"
Grid.Row="1"
Text="{x:Bind ParentCommandName, Mode=OneWay}">
</TextBlock>
<TextBlock
Padding="16"

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -149,16 +149,6 @@
<data name="SettingsValidateErrorTitle" xml:space="preserve">
<value>Encountered errors while loading user settings</value>
</data>
<data name="KeyboardServiceDisabledDialog.PrimaryButtonText" xml:space="preserve">
<value>OK</value>
</data>
<data name="KeyboardServiceDisabledDialog.Title" xml:space="preserve">
<value>Warning:</value>
</data>
<data name="KeyboardServiceWarningText" xml:space="preserve">
<value>The "{0}" isn't running on your machine. This can prevent the Terminal from receiving keyboard input.</value>
<comment>{0} will be replaced with the OS-localized name of the TabletInputService</comment>
</data>
<data name="Ok" xml:space="preserve">
<value>OK</value>
</data>
@@ -482,10 +472,4 @@
<data name="FailedToWriteToSettings" xml:space="preserve">
<value>We could not write to your settings file. Check the permissions on that file to ensure that the read-only flag is not set and that write access is granted.</value>
</data>
<data name="ParentCommandBackButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Back</value>
</data>
<data name="ParentCommandBackButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Back</value>
</data>
</root>

View File

@@ -4,8 +4,8 @@
#include "pch.h"
#include <LibraryResources.h>
#include "ColorPickupFlyout.h"
#include "TerminalTab.h"
#include "TerminalTab.g.cpp"
#include "Tab.h"
#include "Tab.g.cpp"
#include "Utils.h"
#include "ColorHelper.h"
@@ -24,7 +24,7 @@ namespace winrt
namespace winrt::TerminalApp::implementation
{
TerminalTab::TerminalTab(const GUID& profile, const TermControl& control)
Tab::Tab(const GUID& profile, const TermControl& control)
{
_rootPane = std::make_shared<Pane>(profile, control, true);
@@ -36,6 +36,7 @@ namespace winrt::TerminalApp::implementation
Content(_rootPane->GetRootElement());
_MakeTabViewItem();
_MakeSwitchToTabCommand();
_CreateContextMenu();
}
@@ -45,18 +46,18 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::_MakeTabViewItem()
void Tab::_MakeTabViewItem()
{
TabViewItem(::winrt::MUX::Controls::TabViewItem{});
_tabViewItem = ::winrt::MUX::Controls::TabViewItem{};
TabViewItem().DoubleTapped([weakThis = get_weak()](auto&& /*s*/, auto&& /*e*/) {
_tabViewItem.DoubleTapped([weakThis = get_weak()](auto&& /*s*/, auto&& /*e*/) {
if (auto tab{ weakThis.get() })
{
tab->ActivateTabRenamer();
}
});
UpdateTitle();
_UpdateTitle();
_RecalculateAndApplyTabColor();
}
@@ -71,11 +72,22 @@ namespace winrt::TerminalApp::implementation
// Return Value:
// - nullptr if no children were marked `_lastFocused`, else the TermControl
// that was last focused.
TermControl TerminalTab::GetActiveTerminalControl() const
TermControl Tab::GetActiveTerminalControl() const
{
return _activePane->GetTerminalControl();
}
// Method Description:
// - Gets the TabViewItem that represents this Tab
// Arguments:
// - <none>
// Return Value:
// - The TabViewItem that represents this Tab
winrt::MUX::Controls::TabViewItem Tab::GetTabViewItem()
{
return _tabViewItem;
}
// Method Description:
// - Called after construction of a Tab object to bind event handlers to its
// associated Pane and TermControl object
@@ -83,29 +95,39 @@ namespace winrt::TerminalApp::implementation
// - control: reference to the TermControl object to bind event to
// Return Value:
// - <none>
void TerminalTab::Initialize(const TermControl& control)
void Tab::Initialize(const TermControl& control)
{
_BindEventHandlers(control);
}
// Method Description:
// - Returns true if this is the currently focused tab. For any set of tabs,
// there should only be one tab that is marked as focused, though each tab has
// no control over the other tabs in the set.
// Arguments:
// - <none>
// Return Value:
// - true iff this tab is focused.
bool Tab::IsFocused() const noexcept
{
return _focused;
}
// Method Description:
// - Updates our focus state. If we're gaining focus, make sure to transfer
// focus to the last focused terminal control in our tree of controls.
// Arguments:
// - focused: our new focus state
// - focused: our new focus state. If true, we should be focused. If false, we
// should be unfocused.
// Return Value:
// - <none>
void TerminalTab::Focus(WUX::FocusState focusState)
void Tab::SetFocused(const bool focused)
{
_focusState = focusState;
_focused = focused;
if (_focusState != FocusState::Unfocused)
if (_focused)
{
auto lastFocusedControl = GetActiveTerminalControl();
if (lastFocusedControl)
{
lastFocusedControl.Focus(_focusState);
}
_Focus();
}
}
@@ -118,7 +140,7 @@ namespace winrt::TerminalApp::implementation
// Return Value:
// - nullopt if no children of this tab were the last control to be
// focused, else the GUID of the profile of the last control to be focused
std::optional<GUID> TerminalTab::GetFocusedProfile() const noexcept
std::optional<GUID> Tab::GetFocusedProfile() const noexcept
{
return _activePane->GetFocusedProfile();
}
@@ -130,7 +152,7 @@ namespace winrt::TerminalApp::implementation
// - control: reference to the TermControl object to bind event to
// Return Value:
// - <none>
void TerminalTab::_BindEventHandlers(const TermControl& control) noexcept
void Tab::_BindEventHandlers(const TermControl& control) noexcept
{
_AttachEventHandlersToPane(_rootPane);
_AttachEventHandlersToControl(control);
@@ -143,18 +165,35 @@ namespace winrt::TerminalApp::implementation
// - profile: The GUID of the profile these settings should apply to.
// Return Value:
// - <none>
void TerminalTab::UpdateSettings(const TerminalSettings& settings, const GUID& profile)
void Tab::UpdateSettings(const TerminalSettings& settings, const GUID& profile)
{
_rootPane->UpdateSettings(settings, profile);
}
// Method Description:
// - Focus the last focused control in our tree of panes.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::_Focus()
{
_focused = true;
auto lastFocusedControl = GetActiveTerminalControl();
if (lastFocusedControl)
{
lastFocusedControl.Focus(FocusState::Programmatic);
}
}
// Method Description:
// - Set the icon on the TabViewItem for this tab.
// Arguments:
// - iconPath: The new path string to use as the IconPath for our TabViewItem
// Return Value:
// - <none>
winrt::fire_and_forget TerminalTab::UpdateIcon(const winrt::hstring iconPath)
winrt::fire_and_forget Tab::UpdateIcon(const winrt::hstring iconPath)
{
// Don't reload our icon if it hasn't changed.
if (iconPath == _lastIconPath)
@@ -166,13 +205,13 @@ namespace winrt::TerminalApp::implementation
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(TabViewItem().Dispatcher());
co_await winrt::resume_foreground(_tabViewItem.Dispatcher());
if (auto tab{ weakThis.get() })
{
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
Icon(_lastIconPath);
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
IconSource(IconPathConverter::IconSourceWUX(_lastIconPath));
_tabViewItem.IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
// Update SwitchToTab command's icon
SwitchToTabCommand().Icon(_lastIconPath);
@@ -186,7 +225,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - the title string of the last focused terminal control in our tree.
winrt::hstring TerminalTab::_GetActiveTitle() const
winrt::hstring Tab::GetActiveTitle() const
{
if (!_runtimeTabText.empty())
{
@@ -204,14 +243,14 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget TerminalTab::UpdateTitle()
winrt::fire_and_forget Tab::_UpdateTitle()
{
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(TabViewItem().Dispatcher());
co_await winrt::resume_foreground(_tabViewItem.Dispatcher());
if (auto tab{ weakThis.get() })
{
// Bubble our current tab text to anyone who's listening for changes.
Title(_GetActiveTitle());
Title(GetActiveTitle());
// Update SwitchToTab command's name
SwitchToTabCommand().Name(Title());
@@ -229,7 +268,7 @@ namespace winrt::TerminalApp::implementation
// - delta: a number of lines to move the viewport relative to the current viewport.
// Return Value:
// - <none>
winrt::fire_and_forget TerminalTab::Scroll(const int delta)
winrt::fire_and_forget Tab::Scroll(const int delta)
{
auto control = GetActiveTerminalControl();
@@ -245,7 +284,7 @@ namespace winrt::TerminalApp::implementation
// - splitType: The type of split we want to create.
// Return Value:
// - True if the focused pane can be split. False otherwise.
bool TerminalTab::CanSplitPane(SplitState splitType)
bool Tab::CanSplitPane(SplitState splitType)
{
return _activePane->CanSplit(splitType);
}
@@ -259,7 +298,7 @@ namespace winrt::TerminalApp::implementation
// - control: A TermControl to use in the new pane.
// Return Value:
// - <none>
void TerminalTab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control)
void Tab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control)
{
auto [first, second] = _activePane->Split(splitType, profile, control);
_activePane = first;
@@ -279,7 +318,7 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - See Pane::CalcSnappedDimension
float TerminalTab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
float Tab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
{
return _rootPane->CalcSnappedDimension(widthOrHeight, dimension);
}
@@ -291,7 +330,7 @@ namespace winrt::TerminalApp::implementation
// - newSize: the amount of space that the panes have to fill now.
// Return Value:
// - <none>
void TerminalTab::ResizeContent(const winrt::Windows::Foundation::Size& newSize)
void Tab::ResizeContent(const winrt::Windows::Foundation::Size& newSize)
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
@@ -305,7 +344,7 @@ namespace winrt::TerminalApp::implementation
// - direction: The direction to move the separator in.
// Return Value:
// - <none>
void TerminalTab::ResizePane(const Direction& direction)
void Tab::ResizePane(const Direction& direction)
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
@@ -319,7 +358,7 @@ namespace winrt::TerminalApp::implementation
// - direction: The direction to move the focus in.
// Return Value:
// - <none>
void TerminalTab::NavigateFocus(const Direction& direction)
void Tab::NavigateFocus(const Direction& direction)
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
@@ -328,7 +367,7 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
void TerminalTab::Shutdown()
void Tab::Shutdown()
{
_rootPane->Shutdown();
}
@@ -341,21 +380,21 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::ClosePane()
void Tab::ClosePane()
{
_activePane->Close();
}
void TerminalTab::SetTabText(winrt::hstring title)
void Tab::SetTabText(winrt::hstring title)
{
_runtimeTabText = title;
UpdateTitle();
_UpdateTitle();
}
void TerminalTab::ResetTabText()
void Tab::ResetTabText()
{
_runtimeTabText = L"";
UpdateTitle();
_UpdateTitle();
}
// Method Description:
@@ -365,7 +404,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::ActivateTabRenamer()
void Tab::ActivateTabRenamer()
{
_inRename = true;
_receivedKeyDown = false;
@@ -382,7 +421,7 @@ namespace winrt::TerminalApp::implementation
// - control: the TermControl to add events to.
// Return Value:
// - <none>
void TerminalTab::_AttachEventHandlersToControl(const TermControl& control)
void Tab::_AttachEventHandlersToControl(const TermControl& control)
{
auto weakThis{ get_weak() };
@@ -392,7 +431,7 @@ namespace winrt::TerminalApp::implementation
{
// The title of the control changed, but not necessarily the title of the tab.
// Set the tab's text to the active panes' text.
tab->UpdateTitle();
tab->_UpdateTitle();
}
});
@@ -429,7 +468,7 @@ namespace winrt::TerminalApp::implementation
// - pane: a Pane to mark as active.
// Return Value:
// - <none>
void TerminalTab::_UpdateActivePane(std::shared_ptr<Pane> pane)
void Tab::_UpdateActivePane(std::shared_ptr<Pane> pane)
{
// Clear the active state of the entire tree, and mark only the pane as active.
_rootPane->ClearActive();
@@ -437,7 +476,7 @@ namespace winrt::TerminalApp::implementation
_activePane->SetActive();
// Update our own title text to match the newly-active pane.
UpdateTitle();
_UpdateTitle();
// Raise our own ActivePaneChanged event.
_ActivePaneChangedHandlers();
@@ -452,7 +491,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::_AttachEventHandlersToPane(std::shared_ptr<Pane> pane)
void Tab::_AttachEventHandlersToPane(std::shared_ptr<Pane> pane)
{
auto weakThis{ get_weak() };
@@ -492,7 +531,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::_CreateContextMenu()
void Tab::_CreateContextMenu()
{
auto weakThis{ get_weak() };
@@ -566,7 +605,57 @@ namespace winrt::TerminalApp::implementation
newTabFlyout.Items().Append(menuSeparator);
newTabFlyout.Items().Append(_CreateCloseSubMenu());
newTabFlyout.Items().Append(closeTabMenuItem);
TabViewItem().ContextFlyout(newTabFlyout);
_tabViewItem.ContextFlyout(newTabFlyout);
}
// Method Description:
// - Creates a sub-menu containing menu items to close multiple tabs
// Arguments:
// - <none>
// Return Value:
// - the created MenuFlyoutSubItem
Controls::MenuFlyoutSubItem Tab::_CreateCloseSubMenu()
{
auto weakThis{ get_weak() };
// Close tabs after
_closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_CloseTabsAfter();
}
});
_closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
// Close other tabs
_closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_CloseOtherTabs();
}
});
_closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
Controls::MenuFlyoutSubItem closeSubMenu;
closeSubMenu.Text(RS_(L"TabCloseSubMenu"));
closeSubMenu.Items().Append(_closeTabsAfterMenuItem);
closeSubMenu.Items().Append(_closeOtherTabsMenuItem);
return closeSubMenu;
}
// Method Description:
// - Enable the Close menu items based on tab index and total number of tabs
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::_EnableCloseMenuItems()
{
// close other tabs is enabled only if there are other tabs
_closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
// close tabs after is enabled only if there are other tabs on the right
_closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);
}
// Method Description:
@@ -581,9 +670,9 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::_UpdateTabHeader()
void Tab::_UpdateTabHeader()
{
winrt::hstring tabText{ Title() };
winrt::hstring tabText{ GetActiveTitle() };
if (!_inRename)
{
@@ -601,13 +690,13 @@ namespace winrt::TerminalApp::implementation
tb.Text(tabText);
sp.Children().Append(tb);
TabViewItem().Header(sp);
_tabViewItem.Header(sp);
}
else
{
// If we're not currently in the process of renaming the tab,
// then just set the tab's text to whatever our active title is.
TabViewItem().Header(winrt::box_value(tabText));
_tabViewItem.Header(winrt::box_value(tabText));
}
}
else
@@ -624,9 +713,9 @@ namespace winrt::TerminalApp::implementation
// - tabText: This should be the text to initialize the rename text box with.
// Return Value:
// - <none>
void TerminalTab::_ConstructTabRenameBox(const winrt::hstring& tabText)
void Tab::_ConstructTabRenameBox(const winrt::hstring& tabText)
{
if (TabViewItem().Header().try_as<Controls::TextBox>())
if (_tabViewItem.Header().try_as<Controls::TextBox>())
{
return;
}
@@ -674,7 +763,7 @@ namespace winrt::TerminalApp::implementation
{
tab->_runtimeTabText = textBox.Text();
tab->_inRename = false;
tab->UpdateTitle();
tab->_UpdateTitle();
}
});
@@ -704,7 +793,7 @@ namespace winrt::TerminalApp::implementation
e.Handled(true);
textBox.Text(tab->_runtimeTabText);
tab->_inRename = false;
tab->UpdateTitle();
tab->_UpdateTitle();
break;
}
}
@@ -714,7 +803,7 @@ namespace winrt::TerminalApp::implementation
_tabRenameBoxLayoutUpdatedRevoker = tabTextBox.LayoutUpdated(winrt::auto_revoke, [this](auto&&, auto&&) {
// Curiously, the sender for this event is null, so we have to
// get the TextBox from the Tab's Header().
auto textBox{ TabViewItem().Header().try_as<Controls::TextBox>() };
auto textBox{ _tabViewItem.Header().try_as<Controls::TextBox>() };
if (textBox)
{
textBox.SelectAll();
@@ -724,7 +813,7 @@ namespace winrt::TerminalApp::implementation
_tabRenameBoxLayoutUpdatedRevoker.revoke();
});
TabViewItem().Header(tabTextBox);
_tabViewItem.Header(tabTextBox);
}
// Method Description:
@@ -733,7 +822,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - The tab's color, if any
std::optional<winrt::Windows::UI::Color> TerminalTab::GetTabColor()
std::optional<winrt::Windows::UI::Color> Tab::GetTabColor()
{
const auto currControlColor{ GetActiveTerminalControl().TabColor() };
std::optional<winrt::Windows::UI::Color> controlTabColor;
@@ -770,7 +859,7 @@ namespace winrt::TerminalApp::implementation
// - color: the color the user picked for their tab
// Return Value:
// - <none>
void TerminalTab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color)
void Tab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color)
{
_runtimeTabColor.emplace(color);
_RecalculateAndApplyTabColor();
@@ -785,11 +874,11 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::_RecalculateAndApplyTabColor()
void Tab::_RecalculateAndApplyTabColor()
{
auto weakThis{ get_weak() };
TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
_tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
auto ptrTab = weakThis.get();
if (!ptrTab)
return;
@@ -817,7 +906,7 @@ namespace winrt::TerminalApp::implementation
// - color: the color the user picked for their tab
// Return Value:
// - <none>
void TerminalTab::_ApplyTabColor(const winrt::Windows::UI::Color& color)
void Tab::_ApplyTabColor(const winrt::Windows::UI::Color& color)
{
Media::SolidColorBrush selectedTabBrush{};
Media::SolidColorBrush deselectedTabBrush{};
@@ -846,15 +935,15 @@ namespace winrt::TerminalApp::implementation
// currently if a tab has a custom color, a deselected state is
// signified by using the same color with a bit ot transparency
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush);
_tabViewItem.Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush);
_RefreshVisualState();
@@ -869,7 +958,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::ResetRuntimeTabColor()
void Tab::ResetRuntimeTabColor()
{
_runtimeTabColor.reset();
_RecalculateAndApplyTabColor();
@@ -882,7 +971,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::_ClearTabBackgroundColor()
void Tab::_ClearTabBackgroundColor()
{
winrt::hstring keys[] = {
L"TabViewItemHeaderBackground",
@@ -900,9 +989,9 @@ namespace winrt::TerminalApp::implementation
for (auto keyString : keys)
{
auto key = winrt::box_value(keyString);
if (TabViewItem().Resources().HasKey(key))
if (_tabViewItem.Resources().HasKey(key))
{
TabViewItem().Resources().Remove(key);
_tabViewItem.Resources().Remove(key);
}
}
@@ -916,9 +1005,9 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::ActivateColorPicker()
void Tab::ActivateColorPicker()
{
_tabColorPickup.ShowAt(TabViewItem());
_tabColorPickup.ShowAt(_tabViewItem);
}
// Method Description:
@@ -928,17 +1017,17 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
void TerminalTab::_RefreshVisualState()
void Tab::_RefreshVisualState()
{
if (_focusState != FocusState::Unfocused)
if (_focused)
{
VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
VisualStateManager::GoToState(_tabViewItem, L"Normal", true);
VisualStateManager::GoToState(_tabViewItem, L"Selected", true);
}
else
{
VisualStateManager::GoToState(TabViewItem(), L"Selected", true);
VisualStateManager::GoToState(TabViewItem(), L"Normal", true);
VisualStateManager::GoToState(_tabViewItem, L"Selected", true);
VisualStateManager::GoToState(_tabViewItem, L"Normal", true);
}
}
@@ -948,7 +1037,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - The total number of leaf panes hosted by this tab.
int TerminalTab::GetLeafPaneCount() const noexcept
int Tab::GetLeafPaneCount() const noexcept
{
return _rootPane->GetLeafPaneCount();
}
@@ -963,12 +1052,12 @@ namespace winrt::TerminalApp::implementation
// Return Value:
// - The SplitState that we should use for an `Automatic` split given
// `availableSpace`
SplitState TerminalTab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
SplitState Tab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const
{
return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical);
}
bool TerminalTab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const
bool Tab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const
{
return _rootPane->PreCalculateCanSplit(_activePane, splitType, availableSpace).value_or(false);
}
@@ -977,13 +1066,13 @@ namespace winrt::TerminalApp::implementation
// - Toggle our zoom state.
// * If we're not zoomed, then zoom the active pane, making it take the
// full size of the tab. We'll achieve this by changing our response to
// Tab::GetTabContent, so that it'll return the zoomed pane only.
// Tab::GetRootElement, so that it'll return the zoomed pane only.
// * If we're currently zoomed on a pane, un-zoom that pane.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalTab::ToggleZoom()
void Tab::ToggleZoom()
{
if (_zoomedPane)
{
@@ -994,7 +1083,7 @@ namespace winrt::TerminalApp::implementation
EnterZoom();
}
}
void TerminalTab::EnterZoom()
void Tab::EnterZoom()
{
_zoomedPane = _activePane;
_rootPane->Maximize(_zoomedPane);
@@ -1002,7 +1091,7 @@ namespace winrt::TerminalApp::implementation
_UpdateTabHeader();
Content(_zoomedPane->GetRootElement());
}
void TerminalTab::ExitZoom()
void Tab::ExitZoom()
{
_rootPane->Restore(_zoomedPane);
_zoomedPane = nullptr;
@@ -1011,12 +1100,60 @@ namespace winrt::TerminalApp::implementation
Content(_rootPane->GetRootElement());
}
bool TerminalTab::IsZoomed()
bool Tab::IsZoomed()
{
return _zoomedPane != nullptr;
}
DEFINE_EVENT(TerminalTab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
// Method Description:
// - Initializes a SwitchToTab command object for this Tab instance.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Tab::_MakeSwitchToTabCommand()
{
SwitchToTabArgs args{ _TabViewIndex };
ActionAndArgs focusTabAction{ ShortcutAction::SwitchToTab, args };
Command command;
command.Action(focusTabAction);
command.Name(Title());
command.Icon(_lastIconPath);
SwitchToTabCommand(command);
}
void Tab::_CloseTabsAfter()
{
CloseTabsAfterArgs args{ _TabViewIndex };
ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args };
_dispatch.DoAction(closeTabsAfter);
}
void Tab::_CloseOtherTabs()
{
CloseOtherTabsArgs args{ _TabViewIndex };
ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args };
_dispatch.DoAction(closeOtherTabs);
}
void Tab::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs)
{
TabViewIndex(idx);
TabViewNumTabs(numTabs);
_EnableCloseMenuItems();
SwitchToTabCommand().Action().Args().as<SwitchToTabArgs>().TabIndex(idx);
}
void Tab::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
{
_dispatch = dispatch;
}
DEFINE_EVENT(Tab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
DEFINE_EVENT(Tab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
DEFINE_EVENT(Tab, ColorCleared, _colorCleared, winrt::delegate<>);
}

View File

@@ -4,8 +4,7 @@
#pragma once
#include "Pane.h"
#include "ColorPickupFlyout.h"
#include "TabBase.h"
#include "TerminalTab.g.h"
#include "Tab.g.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
@@ -15,18 +14,21 @@ namespace TerminalAppLocalTests
namespace winrt::TerminalApp::implementation
{
struct TerminalTab : TerminalTabT<TerminalTab, TabBase>
struct Tab : public TabT<Tab>
{
public:
TerminalTab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
Tab() = delete;
Tab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
// Called after construction to perform the necessary setup, which relies on weak_ptr
void Initialize(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control);
winrt::Microsoft::UI::Xaml::Controls::TabViewItem GetTabViewItem();
winrt::Microsoft::Terminal::TerminalControl::TermControl GetActiveTerminalControl() const;
std::optional<GUID> GetFocusedProfile() const noexcept;
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
bool IsFocused() const noexcept;
void SetFocused(const bool focused);
winrt::fire_and_forget Scroll(const int delta);
@@ -44,9 +46,9 @@ namespace winrt::TerminalApp::implementation
void NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::Direction& direction);
void UpdateSettings(const winrt::TerminalApp::TerminalSettings& settings, const GUID& profile);
winrt::fire_and_forget UpdateTitle();
winrt::hstring GetActiveTitle() const;
void Shutdown() override;
void Shutdown();
void ClosePane();
void SetTabText(winrt::hstring title);
@@ -66,10 +68,28 @@ namespace winrt::TerminalApp::implementation
int GetLeafPaneCount() const noexcept;
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
DECLARE_EVENT(ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::Controls::IconSource, IconSource, _PropertyChangedHandlers, nullptr);
OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::Command, SwitchToTabCommand, _PropertyChangedHandlers, nullptr);
// The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector.
// This is needed since Tab is going to be managing its own SwitchToTab command.
OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewIndex, _PropertyChangedHandlers, 0);
// The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector.
OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewNumTabs, _PropertyChangedHandlers, 0);
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::UIElement, Content, _PropertyChangedHandlers, nullptr);
private:
std::shared_ptr<Pane> _rootPane{ nullptr };
std::shared_ptr<Pane> _activePane{ nullptr };
@@ -81,7 +101,9 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{};
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{};
bool _focused{ false };
bool _receivedKeyDown{ false };
winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr };
winrt::hstring _runtimeTabText{};
bool _inRename{ false };
@@ -90,8 +112,11 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
void _MakeTabViewItem();
void _Focus();
void _CreateContextMenu() override;
void _CreateContextMenu();
winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _CreateCloseSubMenu();
void _EnableCloseMenuItems();
void _RefreshVisualState();
@@ -102,14 +127,19 @@ namespace winrt::TerminalApp::implementation
void _UpdateActivePane(std::shared_ptr<Pane> pane);
winrt::hstring _GetActiveTitle() const;
void _UpdateTabHeader();
winrt::fire_and_forget _UpdateTitle();
void _ConstructTabRenameBox(const winrt::hstring& tabText);
void _RecalculateAndApplyTabColor();
void _ApplyTabColor(const winrt::Windows::UI::Color& color);
void _ClearTabBackgroundColor();
void _MakeSwitchToTabCommand();
void _CloseTabsAfter();
void _CloseOtherTabs();
friend class ::TerminalAppLocalTests::TabTests;
};
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ShortcutActionDispatch.idl";
namespace TerminalApp
{
[default_interface] runtimeclass Tab : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
String Title { get; };
Windows.UI.Xaml.Controls.IconSource IconSource { get; };
Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand { get; };
UInt32 TabViewIndex { get; };
Windows.UI.Xaml.UIElement Content { get; };
void SetDispatch(ShortcutActionDispatch dispatch);
}
}

View File

@@ -1,148 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include <LibraryResources.h>
#include "TabBase.h"
#include "TabBase.g.cpp"
using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::TerminalControl;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Windows::System;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
}
namespace winrt::TerminalApp::implementation
{
WUX::FocusState TabBase::FocusState() const noexcept
{
return _focusState;
}
// Method Description:
// - Prepares this tab for being removed from the UI hierarchy
void TabBase::Shutdown()
{
Content(nullptr);
_ClosedHandlers(nullptr, nullptr);
}
// Method Description:
// - Creates a context menu attached to the tab.
// Currently contains elements allowing the user to close the selected tab
// Arguments:
// - <none>
// Return Value:
// - <none>
void TabBase::_CreateContextMenu()
{
auto weakThis{ get_weak() };
// Close
Controls::MenuFlyoutItem closeTabMenuItem;
Controls::FontIcon closeSymbol;
closeSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" });
closeSymbol.Glyph(L"\xE8BB");
closeTabMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_ClosedHandlers(nullptr, nullptr);
}
});
closeTabMenuItem.Text(RS_(L"TabClose"));
closeTabMenuItem.Icon(closeSymbol);
// Build the menu
Controls::MenuFlyout newTabFlyout;
newTabFlyout.Items().Append(_CreateCloseSubMenu());
newTabFlyout.Items().Append(closeTabMenuItem);
TabViewItem().ContextFlyout(newTabFlyout);
}
// Method Description:
// - Creates a sub-menu containing menu items to close multiple tabs
// Arguments:
// - <none>
// Return Value:
// - the created MenuFlyoutSubItem
Controls::MenuFlyoutSubItem TabBase::_CreateCloseSubMenu()
{
auto weakThis{ get_weak() };
// Close tabs after
_closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_CloseTabsAfter();
}
});
_closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
// Close other tabs
_closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
if (auto tab{ weakThis.get() })
{
tab->_CloseOtherTabs();
}
});
_closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
Controls::MenuFlyoutSubItem closeSubMenu;
closeSubMenu.Text(RS_(L"TabCloseSubMenu"));
closeSubMenu.Items().Append(_closeTabsAfterMenuItem);
closeSubMenu.Items().Append(_closeOtherTabsMenuItem);
return closeSubMenu;
}
// Method Description:
// - Enable the Close menu items based on tab index and total number of tabs
// Arguments:
// - <none>
// Return Value:
// - <none>
void TabBase::_EnableCloseMenuItems()
{
// close other tabs is enabled only if there are other tabs
_closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
// close tabs after is enabled only if there are other tabs on the right
_closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);
}
void TabBase::_CloseTabsAfter()
{
CloseTabsAfterArgs args{ _TabViewIndex };
ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args };
_dispatch.DoAction(closeTabsAfter);
}
void TabBase::_CloseOtherTabs()
{
CloseOtherTabsArgs args{ _TabViewIndex };
ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args };
_dispatch.DoAction(closeOtherTabs);
}
void TabBase::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs)
{
TabViewIndex(idx);
TabViewNumTabs(numTabs);
_EnableCloseMenuItems();
SwitchToTabCommand().Action().Args().as<SwitchToTabArgs>().TabIndex(idx);
}
void TabBase::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
{
_dispatch = dispatch;
}
}

View File

@@ -1,57 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "inc/cppwinrt_utils.h"
#include "TabBase.g.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
class TabTests;
};
namespace winrt::TerminalApp::implementation
{
struct TabBase : TabBaseT<TabBase>
{
public:
virtual void Focus(winrt::Windows::UI::Xaml::FocusState focusState) = 0;
winrt::Windows::UI::Xaml::FocusState FocusState() const noexcept;
virtual void Shutdown();
void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch);
void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs);
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler<winrt::Windows::Foundation::IInspectable>);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
// The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector.
// This is needed since Tab is going to be managing its own SwitchToTab command.
GETSET_PROPERTY(uint32_t, TabViewIndex, 0);
// The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector.
GETSET_PROPERTY(uint32_t, TabViewNumTabs, 0);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers);
OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::Command, SwitchToTabCommand, _PropertyChangedHandlers, nullptr);
GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr);
protected:
winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused };
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{};
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{};
winrt::TerminalApp::ShortcutActionDispatch _dispatch;
virtual void _CreateContextMenu();
winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _CreateCloseSubMenu();
void _EnableCloseMenuItems();
void _CloseTabsAfter();
void _CloseOtherTabs();
friend class ::TerminalAppLocalTests::TabTests;
};
}

View File

@@ -1,24 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "ShortcutActionDispatch.idl";
namespace TerminalApp
{
unsealed runtimeclass TabBase : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
String Title { get; };
String Icon { get; };
Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand;
Microsoft.UI.Xaml.Controls.TabViewItem TabViewItem { get; };
Windows.UI.Xaml.FrameworkElement Content { get; };
Windows.UI.Xaml.FocusState FocusState { get; };
UInt32 TabViewIndex;
UInt32 TabViewNumTabs;
overridable void Focus(Windows.UI.Xaml.FocusState focusState);
overridable void Shutdown();
void SetDispatch(ShortcutActionDispatch dispatch);
}
}

View File

@@ -70,12 +70,6 @@
<ClInclude Include="MinMaxCloseControl.h">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="TabBase.h">
<DependentUpon>TabBase.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TerminalTab.h">
<DependentUpon>TerminalTab.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TerminalPage.h">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
@@ -101,6 +95,9 @@
<ClInclude Include="IconPathConverter.h">
<DependentUpon>IconPathConverter.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Tab.h">
<DependentUpon>Tab.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Pane.h" />
<ClInclude Include="ColorHelper.h" />
<ClInclude Include="TerminalSettings.h">
@@ -130,12 +127,6 @@
<ClCompile Include="MinMaxCloseControl.cpp">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TabBase.cpp">
<DependentUpon>TabBase.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TerminalTab.cpp">
<DependentUpon>TerminalTab.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TerminalPage.cpp">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
@@ -161,6 +152,9 @@
<ClCompile Include="IconPathConverter.cpp">
<DependentUpon>IconPathConverter.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Tab.cpp">
<DependentUpon>Tab.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Pane.cpp" />
<ClCompile Include="Pane.LayoutSizeNode.cpp" />
<ClCompile Include="ColorHelper.cpp" />
@@ -203,8 +197,6 @@
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="TabBase.idl" />
<Midl Include="TerminalTab.idl" />
<Midl Include="TerminalPage.idl">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
@@ -228,6 +220,7 @@
<Midl Include="EmptyStringVisibilityConverter.idl" />
<Midl Include="HasNestedCommandsVisibilityConverter.idl" />
<Midl Include="IconPathConverter.idl" />
<Midl Include="Tab.idl" />
<Midl Include="TerminalSettings.idl" />
</ItemGroup>
<!-- ========================= Misc Files ======================== -->

View File

@@ -27,10 +27,6 @@
<ClCompile Include="TerminalSettings.cpp">
<Filter>settings</Filter>
</ClCompile>
<ClCompile Include="Jumplist.cpp" />
<ClCompile Include="Tab.cpp">
<Filter>tab</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Utils.h" />
@@ -52,10 +48,6 @@
<ClInclude Include="TerminalSettings.h">
<Filter>settings</Filter>
</ClInclude>
<ClInclude Include="Jumplist.h" />
<ClInclude Include="Tab.h">
<Filter>tab</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="AppLogic.idl">
@@ -70,13 +62,14 @@
<Midl Include="ShortcutActionDispatch.idl">
<Filter>settings</Filter>
</Midl>
<Midl Include="Tab.idl">
<Filter>tab</Filter>
</Midl>
<Midl Include="IDirectKeyListener.idl" />
<Midl Include="CommandKeyChordVisibilityConverter.idl" />
<Midl Include="TerminalSettings.idl">
<Filter>settings</Filter>
</Midl>
<Midl Include="TerminalTab.idl">
<Filter>tab</Filter>
</Midl>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@@ -126,4 +119,4 @@
<Filter>app</Filter>
</ApplicationDefinition>
</ItemGroup>
</Project>
</Project>

View File

@@ -42,7 +42,7 @@ namespace winrt
namespace winrt::TerminalApp::implementation
{
TerminalPage::TerminalPage() :
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::Tab>() },
_mruTabActions{ winrt::single_threaded_vector<Command>() },
_startupActions{ winrt::single_threaded_vector<ActionAndArgs>() }
{
@@ -668,10 +668,8 @@ namespace winrt::TerminalApp::implementation
TermControl term{ settings, connection };
auto newTabImpl = winrt::make_self<TerminalTab>(profileGuid, term);
_MakeSwitchToTabCommand(*newTabImpl, _tabs.Size());
// Add the new tab to the list of our tabs.
auto newTabImpl = winrt::make_self<Tab>(profileGuid, term);
_tabs.Append(*newTabImpl);
_mruTabActions.Append(newTabImpl->SwitchToTabCommand());
@@ -688,8 +686,7 @@ namespace winrt::TerminalApp::implementation
auto weakTab = make_weak(newTabImpl);
// When the tab's active pane changes, we'll want to lookup a new icon
// for it. The Title change will be propagated upwards through the tab's
// PropertyChanged event handler.
// for it, and possibly propagate the title up to the window.
newTabImpl->ActivePaneChanged([weakTab, weakThis{ get_weak() }]() {
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
@@ -698,12 +695,24 @@ namespace winrt::TerminalApp::implementation
{
// Possibly update the icon of the tab.
page->_UpdateTabIcon(*tab);
// Possibly update the title of the tab, window to match the newly
// focused pane.
page->_UpdateTitle(*tab);
}
});
auto tabViewItem = newTabImpl->TabViewItem();
auto tabViewItem = newTabImpl->GetTabViewItem();
_tabView.TabItems().Append(tabViewItem);
_ReapplyCompactTabSize();
// GH#6570
// The TabView does not apply compact sizing to items added after Compact is enabled.
// By forcibly reapplying compact sizing every time we add a new tab, we'll make sure
// that it works.
// Workaround from https://github.com/microsoft/microsoft-ui-xaml/issues/2711
if (_tabView.TabWidthMode() == MUX::Controls::TabViewWidthMode::Compact)
{
_tabView.UpdateLayout();
_tabView.TabWidthMode(MUX::Controls::TabViewWidthMode::Compact);
}
// Set this tab's icon to the icon from the user's profile
const auto profile = _settings.FindProfile(profileGuid);
@@ -914,12 +923,12 @@ namespace winrt::TerminalApp::implementation
// TitleChanged event.
// Arguments:
// - tab: the Tab to update the title for.
void TerminalPage::_UpdateTitle(const TerminalTab& tab)
void TerminalPage::_UpdateTitle(const Tab& tab)
{
auto newTabTitle = tab.Title();
auto newTabTitle = tab.GetActiveTitle();
if (_settings.GlobalSettings().ShowTitleInTitlebar() &&
tab.FocusState() != FocusState::Unfocused)
tab.IsFocused())
{
_titleChangeHandlers(*this, newTabTitle);
}
@@ -930,7 +939,7 @@ namespace winrt::TerminalApp::implementation
// tab's icon to that icon.
// Arguments:
// - tab: the Tab to update the title for.
void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
void TerminalPage::_UpdateTabIcon(Tab& tab)
{
const auto lastFocusedProfileOpt = tab.GetFocusedProfile();
if (lastFocusedProfileOpt.has_value())
@@ -981,32 +990,30 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
try
{
try
{
// TODO: GH#5047 - In the future, we should get the Profile of
// the focused pane, and use that to build a new instance of the
// settings so we can duplicate this tab/pane.
//
// Currently, if the profile doesn't exist anymore in our
// settings, we'll silently do nothing.
//
// In the future, it will be preferable to just duplicate the
// current control's settings, but we can't do that currently,
// because we won't be able to create a new instance of the
// connection without keeping an instance of the original Profile
// object around.
auto focusedTab = _GetStrongTabImpl(*index);
// TODO: GH#5047 - In the future, we should get the Profile of
// the focused pane, and use that to build a new instance of the
// settings so we can duplicate this tab/pane.
//
// Currently, if the profile doesn't exist anymore in our
// settings, we'll silently do nothing.
//
// In the future, it will be preferable to just duplicate the
// current control's settings, but we can't do that currently,
// because we won't be able to create a new instance of the
// connection without keeping an instance of the original Profile
// object around.
const auto& profileGuid = terminalTab->GetFocusedProfile();
if (profileGuid.has_value())
{
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
_CreateNewTabFromSettings(profileGuid.value(), settings);
}
const auto& profileGuid = focusedTab->GetFocusedProfile();
if (profileGuid.has_value())
{
const auto settings{ winrt::make<TerminalSettings>(_settings, profileGuid.value(), *_bindings) };
_CreateNewTabFromSettings(profileGuid.value(), settings);
}
CATCH_LOG();
}
CATCH_LOG();
}
}
@@ -1033,32 +1040,20 @@ namespace winrt::TerminalApp::implementation
{
// Removing the tab from the collection should destroy its control and disconnect its connection,
// but it doesn't always do so. The UI tree may still be holding the control and preventing its destruction.
auto tab{ _tabs.GetAt(tabIndex) };
tab.Shutdown();
auto tab{ _GetStrongTabImpl(tabIndex) };
tab->Shutdown();
uint32_t mruIndex;
if (_mruTabActions.IndexOf(_tabs.GetAt(tabIndex).SwitchToTabCommand(), mruIndex))
{
_mruTabActions.RemoveAt(mruIndex);
CommandPalette().SetTabActions(_mruTabActions);
}
_tabs.RemoveAt(tabIndex);
_tabView.TabItems().RemoveAt(tabIndex);
_UpdateTabIndices();
// If the tab switcher is currently open, update it to reflect the
// current list of tabs.
const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode();
const bool commandPaletteIsVisible = CommandPalette().Visibility() == Visibility::Visible;
if (tabSwitchMode == TabSwitcherMode::MostRecentlyUsed && commandPaletteIsVisible)
{
CommandPalette().SetTabActions(_mruTabActions, false);
}
else if (commandPaletteIsVisible)
{
_UpdatePaletteWithInOrderTabs();
}
// To close the window here, we need to close the hosting window.
if (_tabs.Size() == 0)
{
@@ -1096,8 +1091,8 @@ namespace winrt::TerminalApp::implementation
// here. If we don't, then the TabView will technically not have a
// selected item at all, which can make things like ClosePane not
// work correctly.
auto newSelectedTab{ _tabs.GetAt(newSelectedIndex) };
_tabView.SelectedItem(newSelectedTab.TabViewItem());
auto newSelectedTab{ _GetStrongTabImpl(newSelectedIndex) };
_tabView.SelectedItem(newSelectedTab->GetTabViewItem());
}
// GH#5559 - If we were in the middle of a drag/drop, end it by clearing
@@ -1119,7 +1114,7 @@ namespace winrt::TerminalApp::implementation
// Arguments:
// - term: The newly created TermControl to connect the events for
// - hostingTab: The Tab that's hosting this TermControl instance
void TerminalPage::_RegisterTerminalEvents(TermControl term, TerminalTab& hostingTab)
void TerminalPage::_RegisterTerminalEvents(TermControl term, Tab& hostingTab)
{
// Add an event handler when the terminal's selection wants to be copied.
// When the text buffer data is retrieved, we'll copy the data into the Clipboard
@@ -1149,12 +1144,12 @@ namespace winrt::TerminalApp::implementation
}
else if (args.PropertyName() == L"Content")
{
if (*tab == page->_GetFocusedTab())
if (tab == page->_GetFocusedTab())
{
page->_tabContent.Children().Clear();
page->_tabContent.Children().Append(tab->Content());
tab->Focus(FocusState::Programmatic);
tab->SetFocused(true);
}
}
}
@@ -1165,7 +1160,7 @@ namespace winrt::TerminalApp::implementation
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
if (page && tab && (tab->FocusState() != FocusState::Unfocused))
if (page && tab && tab->IsFocused())
{
page->_SetNonClientAreaColors(color);
}
@@ -1175,7 +1170,7 @@ namespace winrt::TerminalApp::implementation
auto page{ weakThis.get() };
auto tab{ weakTab.get() };
if (page && tab && (tab->FocusState() != FocusState::Unfocused))
if (page && tab && tab->IsFocused())
{
page->_ClearNonClientAreaColors();
}
@@ -1189,83 +1184,36 @@ namespace winrt::TerminalApp::implementation
// _ClearNewTabButtonColor();
}
// Method Description:
// - Updates the command palette (tab switcher) with a list of actions
// reflecting the current in-order list of tabs.
void TerminalPage::_UpdatePaletteWithInOrderTabs()
{
auto tabCommands = winrt::single_threaded_vector<Command>();
for (const auto& tab : _tabs)
{
tabCommands.Append(tab.SwitchToTabCommand());
}
CommandPalette().SetTabActions(tabCommands, true);
}
// Method Description:
// - Sets focus to the tab to the right or left the currently selected tab.
void TerminalPage::_SelectNextTab(const bool bMoveRight)
{
const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode();
const bool useInOrderTabIndex = tabSwitchMode != TabSwitcherMode::MostRecentlyUsed;
if (_settings.GlobalSettings().UseTabSwitcher())
{
CommandPalette().SetTabActions(_mruTabActions);
// First, determine what the index of the newly selected tab should be.
// This changes if we're doing an in-order traversal vs a MRU traversal.
auto newTabIndex = 0;
if (useInOrderTabIndex)
{
// Determine what the next in-order tab index is
if (auto index{ _GetFocusedTabIndex() })
{
uint32_t tabCount = _tabs.Size();
// Wraparound math. By adding tabCount and then calculating
// modulo tabCount, we clamp the values to the range [0,
// tabCount) while still supporting moving leftward from 0 to
// tabCount - 1.
newTabIndex = ((tabCount + *index + (bMoveRight ? 1 : -1)) % tabCount);
}
}
else
{
// Determine what the next "most recently used" index is.
// In this case, our focused tab index (in the MRU ordering) is
// always 0. So, going next should go to index 1, and going prev
// should wrap to the end.
// Since ATS is always MRU, our focused tab index is always 0.
// So, going next should go to index 1, and going prev should wrap to the end.
uint32_t tabCount = _mruTabActions.Size();
newTabIndex = ((tabCount + (bMoveRight ? 1 : -1)) % tabCount);
}
const bool useTabSwitcher = tabSwitchMode != TabSwitcherMode::Disabled;
if (useTabSwitcher)
{
if (useInOrderTabIndex)
{
// Set up the list of in-order tabs
_UpdatePaletteWithInOrderTabs();
}
else
{
// Set up the list of MRU tabs
CommandPalette().SetTabActions(_mruTabActions, true);
}
auto newTabIndex = ((tabCount + (bMoveRight ? 1 : -1)) % tabCount);
if (CommandPalette().Visibility() == Visibility::Visible)
{
// If the tab switcher is currently open, don't change its mode.
// Just select the new tab.
CommandPalette().SelectNextItem(bMoveRight);
}
else
{
// Otherwise, set up the tab switcher in the selected mode, with
// the given ordering, and make it visible.
CommandPalette().EnableTabSwitcherMode(false, newTabIndex);
CommandPalette().Visibility(Visibility::Visible);
}
}
else if (auto index{ _GetFocusedTabIndex() })
{
uint32_t tabCount = _tabs.Size();
// Wraparound math. By adding tabCount and then calculating modulo tabCount,
// we clamp the values to the range [0, tabCount) while still supporting moving
// leftward from 0 to tabCount - 1.
auto newTabIndex = ((tabCount + *index + (bMoveRight ? 1 : -1)) % tabCount);
_SelectTab(newTabIndex);
}
}
@@ -1286,8 +1234,8 @@ namespace winrt::TerminalApp::implementation
{
if (_startupState == StartupState::InStartup)
{
auto tab{ _tabs.GetAt(tabIndex) };
_tabView.SelectedItem(tab.TabViewItem());
auto tab{ _GetStrongTabImpl(tabIndex) };
_tabView.SelectedItem(tab->GetTabViewItem());
_UpdatedSelectedTab(tabIndex);
}
else
@@ -1315,21 +1263,16 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalPage::_UnZoomIfNeeded()
{
if (auto focusedTab = _GetFocusedTab())
auto activeTab = _GetFocusedTab();
if (activeTab && activeTab->IsZoomed())
{
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
if (activeTab->IsZoomed())
{
// Remove the content from the tab first, so Pane::UnZoom can
// re-attach the content to the tree w/in the pane
_tabContent.Children().Clear();
// In ExitZoom, we'll change the Tab's Content(), triggering the
// content changed event, which will re-attach the tab's new content
// root to the tree.
activeTab->ExitZoom();
}
}
// Remove the content from the tab first, so Pane::UnZoom can
// re-attach the content to the tree w/in the pane
_tabContent.Children().Clear();
// In ExitZoom, we'll change the Tab's Content(), triggering the
// content changed event, which will re-attach the tab's new content
// root to the tree.
activeTab->ExitZoom();
}
}
@@ -1345,11 +1288,9 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
{
_UnZoomIfNeeded();
terminalTab->NavigateFocus(direction);
}
auto focusedTab{ _GetStrongTabImpl(*index) };
_UnZoomIfNeeded();
focusedTab->NavigateFocus(direction);
}
}
@@ -1357,12 +1298,13 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
{
return terminalTab->GetActiveTerminalControl();
}
auto focusedTab{ _GetStrongTabImpl(*index) };
return focusedTab->GetActiveTerminalControl();
}
else
{
return nullptr;
}
return nullptr;
}
// Method Description:
@@ -1385,11 +1327,11 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - returns a com_ptr to the currently focused tab. This might return null,
// so make sure to check the result!
winrt::TerminalApp::TabBase TerminalPage::_GetFocusedTab()
winrt::com_ptr<Tab> TerminalPage::_GetFocusedTab()
{
if (auto index{ _GetFocusedTabIndex() })
{
return _tabs.GetAt(*index);
return _GetStrongTabImpl(*index);
}
return nullptr;
}
@@ -1414,8 +1356,8 @@ namespace winrt::TerminalApp::implementation
if (auto page{ weakThis.get() })
{
auto tabToFocus = page->_tabs.GetAt(tabIndex);
_tabView.SelectedItem(tabToFocus.TabViewItem());
auto tab{ _GetStrongTabImpl(tabIndex) };
_tabView.SelectedItem(tab->GetTabViewItem());
}
}
@@ -1437,11 +1379,9 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
{
_UnZoomIfNeeded();
terminalTab->ClosePane();
}
auto focusedTab{ _GetStrongTabImpl(*index) };
_UnZoomIfNeeded();
focusedTab->ClosePane();
}
}
@@ -1482,24 +1422,22 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
auto focusedTab{ _GetStrongTabImpl(*index) };
uint32_t realRowsToScroll;
if (rowsToScroll == nullptr)
{
uint32_t realRowsToScroll;
if (rowsToScroll == nullptr)
{
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
terminalTab->GetActiveTerminalControl().GetViewHeight() :
_systemRowsToScroll;
}
else
{
// use the custom value specified in the command
realRowsToScroll = rowsToScroll.Value();
}
auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll);
terminalTab->Scroll(scrollDelta);
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
focusedTab->GetActiveTerminalControl().GetViewHeight() :
_systemRowsToScroll;
}
else
{
// use the custom value specified in the command
realRowsToScroll = rowsToScroll.Value();
}
auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll);
focusedTab->Scroll(scrollDelta);
}
}
@@ -1532,16 +1470,9 @@ namespace winrt::TerminalApp::implementation
return;
}
auto focusedTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt));
// Do nothing if the focused tab isn't a TerminalTab
if (!focusedTab)
{
return;
}
try
{
auto focusedTab = _GetStrongTabImpl(*indexOpt);
TerminalApp::TerminalSettings controlSettings;
GUID realGuid;
bool profileFound = false;
@@ -1615,11 +1546,9 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
{
_UnZoomIfNeeded();
terminalTab->ResizePane(direction);
}
auto focusedTab{ _GetStrongTabImpl(*index) };
_UnZoomIfNeeded();
focusedTab->ResizePane(direction);
}
}
@@ -1637,13 +1566,11 @@ namespace winrt::TerminalApp::implementation
return;
}
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt)))
{
const auto control = _GetActiveControl();
const auto termHeight = control.GetViewHeight();
auto scrollDelta = _ComputeScrollDelta(scrollDirection, termHeight);
terminalTab->Scroll(scrollDelta);
}
const auto control = _GetActiveControl();
const auto termHeight = control.GetViewHeight();
auto focusedTab{ _GetStrongTabImpl(*indexOpt) };
auto scrollDelta = _ComputeScrollDelta(scrollDirection, termHeight);
focusedTab->Scroll(scrollDelta);
}
// Method Description:
@@ -1754,10 +1681,8 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
{
return terminalTab->CalcSnappedDimension(widthOrHeight, dimension);
}
auto focusedTab{ _GetStrongTabImpl(*index) };
return focusedTab->CalcSnappedDimension(widthOrHeight, dimension);
}
}
return dimension;
@@ -2035,17 +1960,18 @@ namespace winrt::TerminalApp::implementation
// Unfocus all the tabs.
for (auto tab : _tabs)
{
tab.Focus(FocusState::Unfocused);
auto tabImpl{ _GetStrongTabImpl(tab) };
tabImpl->SetFocused(false);
}
if (index >= 0)
{
try
{
auto tab{ _tabs.GetAt(index) };
auto tab{ _GetStrongTabImpl(index) };
_tabContent.Children().Clear();
_tabContent.Children().Append(tab.Content());
_tabContent.Children().Append(tab->Content());
// GH#7409: If the tab switcher is open, then we _don't_ want to
// automatically focus the new tab here. The tab switcher wants
@@ -2059,12 +1985,12 @@ namespace winrt::TerminalApp::implementation
// need to worry about focus getting lost.
if (CommandPalette().Visibility() != Visibility::Visible)
{
tab.Focus(FocusState::Programmatic);
tab->SetFocused(true);
_UpdateMRUTab(index);
}
// Raise an event that our title changed
_titleChangeHandlers(*this, tab.Title());
_titleChangeHandlers(*this, tab->GetActiveTitle());
}
CATCH_LOG();
}
@@ -2099,10 +2025,8 @@ namespace winrt::TerminalApp::implementation
const auto newSize = e.NewSize();
for (auto tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
{
terminalTab->ResizeContent(newSize);
}
auto tabImpl{ _GetStrongTabImpl(tab) };
tabImpl->ResizeContent(newSize);
}
}
@@ -2173,10 +2097,9 @@ namespace winrt::TerminalApp::implementation
for (auto tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
{
terminalTab->UpdateSettings(settings, profileGuid);
}
// Attempt to reload the settings of any panes with this profile
auto tabImpl{ _GetStrongTabImpl(tab) };
tabImpl->UpdateSettings(settings, profileGuid);
}
}
CATCH_LOG();
@@ -2188,17 +2111,11 @@ namespace winrt::TerminalApp::implementation
// anymore, so we can't possibly update its settings.
// Update the icon of the tab for the currently focused profile in that tab.
// Only do this for TerminalTabs. Other types of tabs won't have multiple panes
// and profiles so the Title and Icon will be set once and only once on init.
for (auto tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
{
_UpdateTabIcon(*terminalTab);
// Force the TerminalTab to re-grab its currently active control's title.
terminalTab->UpdateTitle();
}
auto tabImpl{ _GetStrongTabImpl(tab) };
_UpdateTabIcon(*tabImpl);
_UpdateTitle(*tabImpl);
}
auto weakThis{ get_weak() };
@@ -2338,9 +2255,10 @@ namespace winrt::TerminalApp::implementation
for (const auto& tab : _tabs)
{
if (tab.TabViewItem().ContextFlyout())
auto tabImpl{ _GetStrongTabImpl(tab) };
if (tabImpl->GetTabViewItem().ContextFlyout())
{
tab.TabViewItem().ContextFlyout().Hide();
tabImpl->GetTabViewItem().ContextFlyout().Hide();
}
}
}
@@ -2399,6 +2317,32 @@ namespace winrt::TerminalApp::implementation
_alwaysOnTopChangedHandlers(*this, nullptr);
}
// Method Description:
// - Returns a com_ptr to the implementation type of the tab at the given index
// Arguments:
// - index: an unsigned integer index to a tab in _tabs
// Return Value:
// - a com_ptr to the implementation type of the Tab
winrt::com_ptr<Tab> TerminalPage::_GetStrongTabImpl(const uint32_t index) const
{
winrt::com_ptr<Tab> tabImpl;
tabImpl.copy_from(winrt::get_self<Tab>(_tabs.GetAt(index)));
return tabImpl;
}
// Method Description:
// - Returns a com_ptr to the implementation type of the given projected Tab
// Arguments:
// - tab: the projected type of a Tab
// Return Value:
// - a com_ptr to the implementation type of the Tab
winrt::com_ptr<Tab> TerminalPage::_GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const
{
winrt::com_ptr<Tab> tabImpl;
tabImpl.copy_from(winrt::get_self<Tab>(tab));
return tabImpl;
}
// Method Description:
// - Sets the tab split button color when a new tab color is selected
// Arguments:
@@ -2600,7 +2544,7 @@ namespace winrt::TerminalApp::implementation
// Return focus to the active control
if (auto index{ _GetFocusedTabIndex() })
{
_tabs.GetAt(*index).Focus(FocusState::Programmatic);
_GetStrongTabImpl(index.value())->SetFocused(true);
_UpdateMRUTab(index.value());
}
}
@@ -2639,75 +2583,10 @@ namespace winrt::TerminalApp::implementation
const uint32_t size = _tabs.Size();
for (uint32_t i = 0; i < size; ++i)
{
auto tab{ _tabs.GetAt(i) };
auto tabImpl{ winrt::get_self<TabBase>(tab) };
tabImpl->UpdateTabViewIndex(i, size);
_GetStrongTabImpl(i)->UpdateTabViewIndex(i, size);
}
}
// Method Description:
// - Returns a com_ptr to the implementation type of the given tab if it's a TerminalTab.
// If the tab is not a TerminalTab, returns nullptr.
// Arguments:
// - tab: the projected type of a Tab
// Return Value:
// - If the tab is a TerminalTab, a com_ptr to the implementation type.
// If the tab is not a TerminalTab, nullptr
winrt::com_ptr<TerminalTab> TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) const
{
if (auto terminalTab = tab.try_as<TerminalApp::TerminalTab>())
{
winrt::com_ptr<TerminalTab> tabImpl;
tabImpl.copy_from(winrt::get_self<TerminalTab>(terminalTab));
return tabImpl;
}
else
{
return nullptr;
}
}
// Method Description:
// - The TabView does not apply compact sizing to items added after Compact is enabled.
// By forcibly reapplying compact sizing every time we add a new tab, we'll make sure
// that it works.
// Workaround from https://github.com/microsoft/microsoft-ui-xaml/issues/2711
// TODO: Remove this function and its calls when ingesting the above changes.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::_ReapplyCompactTabSize()
{
if (_tabView.TabWidthMode() == MUX::Controls::TabViewWidthMode::Compact)
{
_tabView.UpdateLayout();
_tabView.TabWidthMode(MUX::Controls::TabViewWidthMode::Compact);
}
}
// Method Description:
// - Initializes a SwitchToTab command object for this Tab instance.
// This should be done before the tab is added to the _tabs vector so that
// controls like the CmdPal that observe the vector changes can always expect
// a SwitchToTab command to be available.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::_MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index)
{
SwitchToTabArgs args{ index };
ActionAndArgs focusTabAction{ ShortcutAction::SwitchToTab, args };
Command command;
command.Action(focusTabAction);
command.Name(tab.Title());
command.Icon(tab.Icon());
tab.SwitchToTabCommand(command);
}
// Method Description:
// - Computes the delta for scrolling the tab's viewport.
// Arguments:
@@ -2757,87 +2636,11 @@ namespace winrt::TerminalApp::implementation
{
_mruTabActions.RemoveAt(mruIndex);
_mruTabActions.InsertAt(0, command);
// If the tab switcher is currently open, AND we're using it in
// MRU mode, then update it to reflect the current list of tabs.
const auto tabSwitchMode = _settings.GlobalSettings().TabSwitcherMode();
const bool commandPaletteIsVisible = CommandPalette().Visibility() == Visibility::Visible;
if (tabSwitchMode == TabSwitcherMode::MostRecentlyUsed && commandPaletteIsVisible)
{
CommandPalette().SetTabActions(_mruTabActions, false);
}
CommandPalette().SetTabActions(_mruTabActions);
}
}
}
// Method Description:
// - Displays a dialog stating the "Touch Keyboard and Handwriting Panel
// Service" is disabled.
void TerminalPage::ShowKeyboardServiceWarning()
{
if (auto presenter{ _dialogPresenter.get() })
{
presenter.ShowDialog(FindName(L"KeyboardServiceDisabledDialog").try_as<WUX::Controls::ContentDialog>());
}
}
// Function Description:
// - Helper function to get the OS-localized name for the "Touch Keyboard
// and Handwriting Panel Service". If we can't open up the service for any
// reason, then we'll just return the service's key, "TabletInputService".
// Return Value:
// - The OS-localized name for the TabletInputService
winrt::hstring _getTabletServiceName()
{
auto isUwp = false;
try
{
isUwp = ::winrt::Windows::UI::Xaml::Application::Current().as<::winrt::TerminalApp::App>().Logic().IsUwp();
}
CATCH_LOG();
if (isUwp)
{
return winrt::hstring{ TabletInputServiceKey };
}
wil::unique_schandle hManager{ OpenSCManager(nullptr, nullptr, 0) };
if (LOG_LAST_ERROR_IF(!hManager.is_valid()))
{
return winrt::hstring{ TabletInputServiceKey };
}
DWORD cchBuffer = 0;
GetServiceDisplayName(hManager.get(), TabletInputServiceKey.data(), nullptr, &cchBuffer);
std::wstring buffer;
cchBuffer += 1; // Add space for a null
buffer.resize(cchBuffer);
if (LOG_LAST_ERROR_IF(!GetServiceDisplayName(hManager.get(),
TabletInputServiceKey.data(),
buffer.data(),
&cchBuffer)))
{
return winrt::hstring{ TabletInputServiceKey };
}
return winrt::hstring{ buffer };
}
// Method Description:
// - Return the fully-formed warning message for the
// "KeyboardServiceDisabled" dialog. This dialog is used to warn the user
// if the keyboard service is disabled, and uses the OS localization for
// the service's actual name. It's bound to the dialog in XAML.
// Return Value:
// - The warning message, including the OS-localized service name.
winrt::hstring TerminalPage::KeyboardServiceDisabledText()
{
const winrt::hstring serviceName{ _getTabletServiceName() };
const winrt::hstring text{ fmt::format(std::wstring_view(RS_(L"KeyboardServiceWarningText")), serviceName) };
return text;
}
// -------------------------------- WinRT Events ---------------------------------
// Winrt events need a method for adding a callback to the event and removing the callback.
// These macros will define them both for you.

View File

@@ -4,7 +4,7 @@
#pragma once
#include "TerminalPage.g.h"
#include "TerminalTab.h"
#include "Tab.h"
#include "AppKeyBindings.h"
#include "TerminalSettings.h"
@@ -13,7 +13,7 @@
#include "AppCommandlineArgs.h"
static constexpr uint32_t DefaultRowsToScroll{ 3 };
static constexpr std::wstring_view TabletInputServiceKey{ L"TabletInputService" };
// fwdecl unittest classes
namespace TerminalAppLocalTests
{
@@ -71,9 +71,6 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::IDialogPresenter DialogPresenter() const;
void DialogPresenter(winrt::TerminalApp::IDialogPresenter dialogPresenter);
void ShowKeyboardServiceWarning();
winrt::hstring KeyboardServiceDisabledText();
// -------------------------------- WinRT Events ---------------------------------
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring);
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs);
@@ -99,10 +96,10 @@ namespace winrt::TerminalApp::implementation
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
Windows::Foundation::Collections::IObservableVector<TerminalApp::TabBase> _tabs;
Windows::Foundation::Collections::IObservableVector<TerminalApp::Tab> _tabs;
Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> _mruTabActions;
winrt::com_ptr<TerminalTab> _GetTerminalTabImpl(const TerminalApp::TabBase& tab) const;
winrt::com_ptr<Tab> _GetStrongTabImpl(const uint32_t index) const;
winrt::com_ptr<Tab> _GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const;
void _UpdateTabIndices();
bool _isInFocusMode{ false };
@@ -149,12 +146,11 @@ namespace winrt::TerminalApp::implementation
void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap) noexcept;
void _RegisterActionCallbacks();
void _UpdateTitle(const TerminalTab& tab);
void _UpdateTabIcon(TerminalTab& tab);
void _UpdateTitle(const Tab& tab);
void _UpdateTabIcon(Tab& tab);
void _UpdateTabView();
void _UpdateTabWidthMode();
void _UpdateCommandsForPalette();
void _UpdatePaletteWithInOrderTabs();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, Microsoft::Terminal::Settings::Model::Command> _ExpandCommands(Windows::Foundation::Collections::IMapView<winrt::hstring, Microsoft::Terminal::Settings::Model::Command> commandsToExpand,
Windows::Foundation::Collections::IVectorView<Microsoft::Terminal::Settings::Model::Profile> profiles,
Windows::Foundation::Collections::IMapView<winrt::hstring, Microsoft::Terminal::Settings::Model::ColorScheme> schemes);
@@ -163,7 +159,7 @@ namespace winrt::TerminalApp::implementation
void _RemoveTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem);
void _RemoveTabViewItemByIndex(uint32_t tabIndex);
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, TerminalTab& hostingTab);
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, Tab& hostingTab);
void _SelectNextTab(const bool bMoveRight);
bool _SelectTab(const uint32_t tabIndex);
@@ -171,7 +167,7 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetActiveControl();
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;
TerminalApp::TabBase _GetFocusedTab();
winrt::com_ptr<Tab> _GetFocusedTab();
winrt::fire_and_forget _SetFocusedTabIndex(const uint32_t tabIndex);
void _CloseFocusedTab();
void _CloseFocusedPane();
@@ -222,12 +218,6 @@ namespace winrt::TerminalApp::implementation
void _UnZoomIfNeeded();
void _OpenSettingsUI();
void _ReapplyCompactTabSize();
void _MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index);
static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll);
static uint32_t _ReadSystemRowsToScroll();

View File

@@ -26,8 +26,6 @@ namespace TerminalApp
// that there's only one application-global dialog visible at a time,
// and because of GH#5224.
IDialogPresenter DialogPresenter;
void ShowKeyboardServiceWarning();
String KeyboardServiceDisabledText { get; };
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;

View File

@@ -83,20 +83,6 @@ the MIT License. See LICENSE in the project root for license information. -->
</TextBlock>
</ContentDialog>
<ContentDialog
x:Load="False"
x:Name="KeyboardServiceDisabledDialog"
x:Uid="KeyboardServiceDisabledDialog"
DefaultButton="Primary">
<TextBlock
Foreground="{ThemeResource ErrorTextBrush}"
IsTextSelectionEnabled="True"
TextWrapping="WrapWholeWords"
Text="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}" />
</ContentDialog>
<local:CommandPalette
x:Name="CommandPalette"
Grid.Row="1"

View File

@@ -1,11 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TabBase.idl";
namespace TerminalApp
{
[default_interface] runtimeclass TerminalTab : TabBase
{
}
}

View File

@@ -30,8 +30,7 @@
<ClInclude Include="../TitlebarControl.h" />
<ClInclude Include="../TabRowControl.h" />
<ClInclude Include="../App.h" />
<ClInclude Include="../TerminalTab.h" />
<ClInclude Include="../SettingsTab.h" />
<ClInclude Include="../Tab.h" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>

View File

@@ -587,21 +587,21 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// This event is only registered during terminal initialization,
// so we don't need to check _initializedTerminal.
// We also don't lock for things that come back from the renderer.
auto chainHandle = _renderEngine->GetSwapChainHandle();
auto chain = _renderEngine->GetSwapChain();
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
if (auto control{ weakThis.get() })
{
_AttachDxgiSwapChainToXaml(chainHandle);
_AttachDxgiSwapChainToXaml(chain.Get());
}
}
void TermControl::_AttachDxgiSwapChainToXaml(HANDLE swapChainHandle)
void TermControl::_AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain)
{
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative2>();
nativePanel->SetSwapChainHandle(swapChainHandle);
auto nativePanel = SwapChainPanel().as<ISwapChainPanelNative>();
nativePanel->SetSwapChain(swapChain);
}
bool TermControl::_InitializeTerminal()
@@ -705,7 +705,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
THROW_IF_FAILED(dxEngine->Enable());
_renderEngine = std::move(dxEngine);
_AttachDxgiSwapChainToXaml(_renderEngine->GetSwapChainHandle());
_AttachDxgiSwapChainToXaml(_renderEngine->GetSwapChain().Get());
// Tell the DX Engine to notify us when the swap chain changes.
// We do this after we initially set the swapchain so as to avoid unnecessary callbacks (and locking problems)

View File

@@ -108,7 +108,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void ToggleRetroEffect();
winrt::fire_and_forget RenderEngineSwapChainChanged();
void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle);
void _AttachDxgiSwapChainToXaml(IDXGISwapChain1* swapChain);
winrt::fire_and_forget _RendererEnteredErrorState();
void _RenderRetryButton_Click(IInspectable const& button, IInspectable const& args);

View File

@@ -323,7 +323,6 @@ winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings CascadiaSettings::
resultPtr->_ParseJsonString(DefaultJson, true);
resultPtr->LayerJson(resultPtr->_defaultSettings);
resultPtr->_ResolveDefaultProfile();
resultPtr->_UpdateActiveProfiles();
return *resultPtr;
}

View File

@@ -37,8 +37,7 @@ static constexpr std::string_view ConfirmCloseAllKey{ "confirmCloseAllTabs" };
static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" };
static constexpr std::string_view EnableStartupTaskKey{ "startOnUserLogin" };
static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" };
static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" };
static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" };
static constexpr std::string_view UseTabSwitcherKey{ "useTabSwitcher" };
static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" };
static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };
@@ -108,7 +107,7 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_DebugFeaturesEnabled = _DebugFeaturesEnabled;
globals->_StartOnUserLogin = _StartOnUserLogin;
globals->_AlwaysOnTop = _AlwaysOnTop;
globals->_TabSwitcherMode = _TabSwitcherMode;
globals->_UseTabSwitcher = _UseTabSwitcher;
globals->_DisableAnimations = _DisableAnimations;
globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile;
@@ -287,11 +286,7 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop);
// GH#8076 - when adding enum values to this key, we also changed it from
// "useTabSwitcher" to "tabSwitcherMode". Continue supporting
// "useTabSwitcher", but prefer "tabSwitcherMode"
JsonUtils::GetValueForKey(json, LegacyUseTabSwitcherModeKey, _TabSwitcherMode);
JsonUtils::GetValueForKey(json, TabSwitcherModeKey, _TabSwitcherMode);
JsonUtils::GetValueForKey(json, UseTabSwitcherKey, _UseTabSwitcher);
JsonUtils::GetValueForKey(json, DisableAnimationsKey, _DisableAnimations);

View File

@@ -81,7 +81,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
GETSET_SETTING(bool, DebugFeaturesEnabled); // default value set in constructor
GETSET_SETTING(bool, StartOnUserLogin, false);
GETSET_SETTING(bool, AlwaysOnTop, false);
GETSET_SETTING(Model::TabSwitcherMode, TabSwitcherMode, Model::TabSwitcherMode::MostRecentlyUsed);
GETSET_SETTING(bool, UseTabSwitcher, true);
GETSET_SETTING(bool, DisableAnimations, false);
private:

View File

@@ -26,13 +26,6 @@ namespace Microsoft.Terminal.Settings.Model
MaximizedFocusMode,
};
enum TabSwitcherMode
{
MostRecentlyUsed,
InOrder,
Disabled,
};
[default_interface] runtimeclass GlobalAppSettings {
Guid DefaultProfile;
Boolean HasUnparsedDefaultProfile();
@@ -127,9 +120,9 @@ namespace Microsoft.Terminal.Settings.Model
void ClearAlwaysOnTop();
Boolean AlwaysOnTop;
Boolean HasTabSwitcherMode();
void ClearTabSwitcherMode();
TabSwitcherMode TabSwitcherMode;
Boolean HasUseTabSwitcher();
void ClearUseTabSwitcher();
Boolean UseTabSwitcher;
Boolean HasDisableAnimations();
void ClearDisableAnimations();

View File

@@ -342,26 +342,3 @@ JSON_ENUM_MAPPER(::winrt::Windows::System::VirtualKey)
pair_type{ "shift", ValueType::Shift },
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::TabSwitcherMode)
{
JSON_MAPPINGS(3) = {
pair_type{ "mru", ValueType::MostRecentlyUsed },
pair_type{ "inOrder", ValueType::InOrder },
pair_type{ "disabled", ValueType::Disabled },
};
auto FromJson(const Json::Value& json)
{
if (json.isBool())
{
return json.asBool() ? ValueType::MostRecentlyUsed : ValueType::Disabled;
}
return BaseEnumMapper::FromJson(json);
}
bool CanConvert(const Json::Value& json)
{
return BaseEnumMapper::CanConvert(json) || json.isBool();
}
};

View File

@@ -18,7 +18,7 @@
"showTabsInTitlebar": true,
"showTerminalTitleInTitlebar": true,
"tabWidthMode": "equal",
"useTabSwitcher": "mru",
"useTabSwitcher": true,
// Miscellaneous
"confirmCloseAllTabs": true,

View File

@@ -109,63 +109,62 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
// Make sure to call this so we get WM_POINTER messages.
EnableMouseInPointer(true);
auto mainLoop = []() {
// !!! LOAD BEARING !!!
// We must initialize the main thread as a single-threaded apartment before
// constructing any Xaml objects. Failing to do so will cause some issues
// in accessibility somewhere down the line when a UIAutomation object will
// be queried on the wrong thread at the wrong time.
// We used to initialize as STA only _after_ initializing the application
// host, which loaded the settings. The settings needed to be loaded in MTA
// because we were using the Windows.Storage APIs. Since we're no longer
// doing that, we can safely init as STA before any WinRT dispatches.
winrt::init_apartment(winrt::apartment_type::single_threaded);
// !!! LOAD BEARING !!!
// We must initialize the main thread as a single-threaded apartment before
// constructing any Xaml objects. Failing to do so will cause some issues
// in accessibility somewhere down the line when a UIAutomation object will
// be queried on the wrong thread at the wrong time.
// We used to initialize as STA only _after_ initializing the application
// host, which loaded the settings. The settings needed to be loaded in MTA
// because we were using the Windows.Storage APIs. Since we're no longer
// doing that, we can safely init as STA before any WinRT dispatches.
winrt::init_apartment(winrt::apartment_type::single_threaded);
// Create the AppHost object, which will create both the window and the
// Terminal App. This MUST BE constructed before the Xaml manager as TermApp
// provides an implementation of Windows.UI.Xaml.Application.
AppHost host;
// Create the AppHost object, which will create both the window and the
// Terminal App. This MUST BE constructed before the Xaml manager as TermApp
// provides an implementation of Windows.UI.Xaml.Application.
AppHost host;
// Initialize the xaml content. This must be called AFTER the
// WindowsXamlManager is initialized.
host.Initialize();
// Initialize the xaml content. This must be called AFTER the
// WindowsXamlManager is initialized.
host.Initialize();
MSG message;
MSG message;
while (GetMessage(&message, nullptr, 0, 0))
while (GetMessage(&message, nullptr, 0, 0))
{
// GH#638 (Pressing F7 brings up both the history AND a caret browsing message)
// The Xaml input stack doesn't allow an application to suppress the "caret browsing"
// dialog experience triggered when you press F7. Official recommendation from the Xaml
// team is to catch F7 before we hand it off.
// AppLogic contains an ad-hoc implementation of event bubbling for a runtime classes
// implementing a custom IF7Listener interface.
// If the recipient of IF7Listener::OnF7Pressed suggests that the F7 press has, in fact,
// been handled we can discard the message before we even translate it.
if (_messageIsF7Keypress(message))
{
if (host.OnDirectKeyEvent(VK_F7, LOBYTE(HIWORD(message.lParam)), true))
{
if (host.OnDirectKeyEvent(VK_F7, true))
{
// The application consumed the F7. Don't let Xaml get it.
continue;
}
// The application consumed the F7. Don't let Xaml get it.
continue;
}
}
// GH#6421 - System XAML will never send an Alt KeyUp event. So, similar
// to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp
// here, and plumb it through.
if (_messageIsAltKeyup(message))
// GH#6421 - System XAML will never send an Alt KeyUp event. So, similar
// to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp
// here, and plumb it through.
if (_messageIsAltKeyup(message))
{
// Let's pass <Alt> to the application
if (host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false))
{
// Let's pass <Alt> to the application
if (host.OnDirectKeyEvent(VK_MENU, LOBYTE(HIWORD(message.lParam)), false))
{
// Let's pass <Alt> to the application
if (host.OnDirectKeyEvent(VK_MENU, false))
{
// The application consumed the Alt. Don't let Xaml get it.
continue;
}
}
TranslateMessage(&message);
DispatchMessage(&message);
// The application consumed the Alt. Don't let Xaml get it.
continue;
}
};
}
std::thread t{ mainLoop };
mainLoop();
return 0;
TranslateMessage(&message);
DispatchMessage(&message);
}
return 0;
}

View File

@@ -82,7 +82,7 @@ public:
winrt::event_token name(args const& handler) { return _##name##Handlers.add(handler); } \
void name(winrt::event_token const& token) { _##name##Handlers.remove(token); } \
\
protected: \
private: \
winrt::event<args> _##name##Handlers;
// This is a helper macro for both declaring the signature and body of an event
@@ -128,7 +128,7 @@ private:
// (like when the class is being initialized).
#define OBSERVABLE_GETSET_PROPERTY(type, name, event, ...) \
public: \
type name() const noexcept { return _##name; }; \
type name() { return _##name; }; \
void name(const type& value) \
{ \
if (_##name != value) \

View File

@@ -13,7 +13,7 @@
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<AdditionalDependencies>onecoreuap_apiset.lib;d3dcompiler.lib;dwmapi.lib;uxtheme.lib;shlwapi.lib;ntdll.lib;dcomp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>onecoreuap_apiset.lib;d3dcompiler.lib;dwmapi.lib;uxtheme.lib;shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>

View File

@@ -47,6 +47,7 @@ class AliasTests
TEST_METHOD_PROPERTY(L"Data:bUnicode", L"{FALSE, TRUE}")
TEST_METHOD_PROPERTY(L"Data:bSetFirst", L"{FALSE, TRUE}")
END_TEST_METHOD()
};
// Caller must free ppsz if not null.

View File

@@ -3,8 +3,6 @@
#include "precomp.h"
#include <thread>
#include "..\..\interactivity\onecore\SystemConfigurationProvider.hpp"
// some assumptions have been made on this value. only change it if you have a good reason to.
@@ -13,6 +11,7 @@
using WEX::Logging::Log;
using namespace WEX::Common;
using namespace WEX::TestExecution;
// This class is intended to test:
// FlushConsoleInputBuffer
@@ -21,6 +20,7 @@ using namespace WEX::Common;
// WriteConsoleInput
// GetNumberOfConsoleInputEvents
// GetNumberOfConsoleMouseButtons
// ReadConsoleA
class InputTests
{
BEGIN_TEST_CLASS(InputTests)
@@ -54,6 +54,44 @@ class InputTests
BEGIN_TEST_METHOD(TestVtInputGeneration)
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
END_TEST_METHOD();
BEGIN_TEST_METHOD(TestCookedAliasProcessing)
TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
END_TEST_METHOD()
BEGIN_TEST_METHOD(TestCookedTextEntry)
TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
END_TEST_METHOD()
BEGIN_TEST_METHOD(TestCookedAlphaPermutations)
TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
TEST_METHOD_PROPERTY(L"Data:inputcp", L"{437, 932}")
TEST_METHOD_PROPERTY(L"Data:outputcp", L"{437, 932}")
TEST_METHOD_PROPERTY(L"Data:inputmode", L"{487, 481}") // 487 is 0x1e7, 481 is 0x1e1 (ENABLE_LINE_INPUT on/off)
TEST_METHOD_PROPERTY(L"Data:outputmode", L"{7}")
TEST_METHOD_PROPERTY(L"Data:font", L"{Consolas, MS Gothic}")
END_TEST_METHOD()
BEGIN_TEST_METHOD(TestReadCharByChar)
TEST_METHOD_PROPERTY(L"Data:readmode", L"{cooked, raw, direct}")
//TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
END_TEST_METHOD()
BEGIN_TEST_METHOD(TestReadLeadTrailString)
TEST_METHOD_PROPERTY(L"Data:readmode", L"{cooked, raw, direct}")
//TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
END_TEST_METHOD()
BEGIN_TEST_METHOD(TestReadChangeCodepageInMiddle)
TEST_METHOD_PROPERTY(L"Data:readmode", L"{cooked, raw, direct}")
//TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
END_TEST_METHOD()
BEGIN_TEST_METHOD(TestReadChangeCodepageBetweenBytes)
TEST_METHOD_PROPERTY(L"Data:readmode", L"{cooked, raw, direct}")
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
//TEST_METHOD_PROPERTY(L"TestTimeout", L"00:01:00")
END_TEST_METHOD()
};
void VerifyNumberOfInputRecords(const HANDLE hConsoleInput, _In_ DWORD nInputs)
@@ -716,3 +754,836 @@ void InputTests::RawReadUnpacksCoalescedInputRecords()
VERIFY_WIN32_BOOL_SUCCEEDED(GetNumberOfConsoleInputEvents(hIn, &eventCount));
VERIFY_ARE_EQUAL(eventCount, static_cast<DWORD>(0));
}
static std::vector<INPUT_RECORD> _stringToInputs(std::wstring_view wstr)
{
std::vector<INPUT_RECORD> result;
for (const auto& wch : wstr)
{
INPUT_RECORD ir = { 0 };
ir.EventType = KEY_EVENT;
ir.Event.KeyEvent.bKeyDown = TRUE;
ir.Event.KeyEvent.dwControlKeyState = 0;
ir.Event.KeyEvent.uChar.UnicodeChar = wch;
ir.Event.KeyEvent.wRepeatCount = 1;
ir.Event.KeyEvent.wVirtualKeyCode = VkKeyScanW(wch);
ir.Event.KeyEvent.wVirtualScanCode = gsl::narrow<WORD>(MapVirtualKeyW(ir.Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC));
result.emplace_back(ir);
ir.Event.KeyEvent.bKeyDown = FALSE;
result.emplace_back(ir);
}
return result;
}
static HRESULT _sendStringToInput(HANDLE in, std::wstring_view wstr)
{
auto records = _stringToInputs(wstr);
DWORD written;
RETURN_IF_WIN32_BOOL_FALSE(WriteConsoleInputW(in, records.data(), gsl::narrow<DWORD>(records.size()), &written));
return S_OK;
}
// Routine Description:
// - Reads data from the standard input with a 5 second timeout
// Arguments:
// - in - The standard input handle
// - buf - The buffer to use. On in, this is the max size we'll read. On out, it's resized to fit.
// - async - Whether to read async, default to true. Reading async will put a 5 second timeout on the read.
// Return Value:
// - S_OK or an error from ReadConsole/threading timeout.
static HRESULT _readStringFromInput(HANDLE in, std::string& buf, bool async = true)
{
DWORD read = 0;
if (async)
{
auto tryRead = std::async(std::launch::async, [&] {
return _readStringFromInput(in, buf, false); // just re-enter ourselves on the other thread as sync.
});
if (std::future_status::ready != tryRead.wait_for(std::chrono::seconds{ 5 }))
{
// Shove something into the input to unstick it then fail.
_sendStringToInput(in, L"a\r\n");
RETURN_NTSTATUS(STATUS_TIMEOUT);
// If somehow this still isn't enough to unstick the thread, be sure to set
// the whole test timeout is 1 min in the parameters/metadata at the top.
}
else
{
return tryRead.get();
}
}
else
{
RETURN_IF_WIN32_BOOL_FALSE(ReadConsoleA(in, buf.data(), gsl::narrow<DWORD>(buf.size()), &read, nullptr));
// If we successfully read, then resize to fit the buffer.
buf.resize(read);
return S_OK;
}
}
static HRESULT _readStringFromInputDirect(HANDLE in, std::string& buf, bool async = true)
{
if (async)
{
auto tryRead = std::async(std::launch::async, [&] {
return _readStringFromInputDirect(in, buf, false); // just re-enter ourselves on the other thread as sync.
});
if (std::future_status::ready != tryRead.wait_for(std::chrono::seconds{ 5 }))
{
// Shove something into the input to unstick it then fail.
_sendStringToInput(in, L"a\r\n");
RETURN_NTSTATUS(STATUS_TIMEOUT);
// If somehow this still isn't enough to unstick the thread, be sure to set
// the whole test timeout is 1 min in the parameters/metadata at the top.
}
else
{
return tryRead.get();
}
}
else
{
const auto originalSize = buf.size();
buf.clear();
std::vector<INPUT_RECORD> ir;
DWORD read = 0;
do
{
ir.clear();
ir.resize(originalSize - buf.size());
RETURN_IF_WIN32_BOOL_FALSE(ReadConsoleInputA(in, ir.data(), gsl::narrow_cast<DWORD>(ir.size()), &read));
for (const auto& r : ir)
{
if (r.EventType == KEY_EVENT)
{
if (!r.Event.KeyEvent.bKeyDown)
{
buf.push_back(r.Event.KeyEvent.uChar.AsciiChar);
}
}
}
ir.clear();
} while (originalSize > buf.size());
return S_OK;
}
}
void InputTests::TestCookedAliasProcessing()
{
const auto in = GetStdInputHandle();
DWORD originalInMode = 0;
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(in, &originalInMode));
DWORD originalCodepage = GetConsoleCP();
auto restoreInModeOnExit = wil::scope_exit([&] {
SetConsoleMode(in, originalInMode);
SetConsoleCP(originalCodepage);
});
const DWORD testInMode = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(in, testInMode));
auto modulePath = wil::GetModuleFileNameW<std::wstring>(nullptr);
std::filesystem::path path{ modulePath };
auto fileName = path.filename();
auto exeName = fileName.wstring();
VERIFY_WIN32_BOOL_SUCCEEDED(AddConsoleAliasW(L"foo", L"echo bar$Techo baz$Techo bam", exeName.data()));
std::wstring commandWritten = L"foo\r\n";
std::queue<std::string> commandExpected;
commandExpected.push("echo bar\r");
commandExpected.push("echo baz\r");
commandExpected.push("echo bam\r");
VERIFY_SUCCEEDED(_sendStringToInput(in, commandWritten));
std::string buf;
while (!commandExpected.empty())
{
buf.resize(500);
VERIFY_SUCCEEDED(_readStringFromInput(in, buf));
auto actual = buf;
auto expected = commandExpected.front();
commandExpected.pop();
VERIFY_ARE_EQUAL(expected, actual);
}
}
void InputTests::TestCookedTextEntry()
{
const auto in = GetStdInputHandle();
DWORD originalInMode = 0;
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(in, &originalInMode));
DWORD originalCodepage = GetConsoleCP();
auto restoreInModeOnExit = wil::scope_exit([&] {
SetConsoleMode(in, originalInMode);
SetConsoleCP(originalCodepage);
});
const DWORD testInMode = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(in, testInMode));
std::wstring commandWritten = L"foo\r\n";
std::queue<std::string> commandExpected;
commandExpected.push("foo\r\n");
VERIFY_SUCCEEDED(_sendStringToInput(in, commandWritten));
std::string buf;
while (!commandExpected.empty())
{
buf.resize(500);
VERIFY_SUCCEEDED(_readStringFromInput(in, buf));
auto actual = buf;
auto expected = commandExpected.front();
commandExpected.pop();
VERIFY_ARE_EQUAL(expected, actual);
}
}
// Greek letters, lowercase...
const std::array<std::wstring, 4> wide = {
L"\u03b1", // alpha
L"\u03b2", // beta
// no gamma because it doesn't translate to 437
L"\u03b4", // delta
L"\u03b5" //epsilon
};
const std::array<std::string, 4> char437 = {
"\xe0",
"\xe1",
"\xeb",
"\xee"
};
const std::array<std::string, 4> char932 = {
"\x83\xbf",
"\x83\xc0",
"\x83\xc2",
"\x83\xc3"
};
const std::wstring widecrlf = L"\r\n";
const std::string crlf = "\r\n";
enum class ReadMode
{
Cooked, // ReadConsoleA with ENABLE_LINE_INPUT
Raw, // ReadConsoleA without ENABLE_LINE_INPUT
Direct // ReadConsoleInputA
};
static HRESULT _readString(HANDLE in, ReadMode mode, std::string& buf, bool async = true)
{
switch (mode)
{
case ReadMode::Cooked:
case ReadMode::Raw:
return _readStringFromInput(in, buf, async);
case ReadMode::Direct:
return _readStringFromInputDirect(in, buf, async);
default:
VERIFY_FAIL(L"Not supported");
return E_NOTIMPL;
}
}
void InputTests::TestCookedAlphaPermutations()
{
DWORD inputcp, outputcp, inputmode, outputmode;
String font;
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"inputcp", inputcp), L"Get input cp");
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"outputcp", outputcp), L"Get output cp");
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"inputmode", inputmode), L"Get input mode");
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"outputmode", outputmode), L"Get output mode");
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"font", font), L"Get font");
std::wstring wstrFont{ font };
if (wstrFont == L"MS Gothic")
{
// MS Gothic... but in full width characters and the katakana representation...
// MS GOSHIKKU romanized...
wstrFont = L"\xff2d\xff33\x0020\x30b4\x30b7\x30c3\x30af";
}
const auto in = GetStdInputHandle();
const auto out = GetStdOutputHandle();
Log::Comment(L"Backup original modes and codepages and font.");
DWORD originalInMode, originalOutMode, originalInputCP, originalOutputCP;
CONSOLE_FONT_INFOEX originalFont = { 0 };
originalFont.cbSize = sizeof(originalFont);
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(in, &originalInMode));
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(out, &originalOutMode));
originalInputCP = GetConsoleCP();
originalOutputCP = GetConsoleOutputCP();
VERIFY_WIN32_BOOL_SUCCEEDED(GetCurrentConsoleFontEx(out, FALSE, &originalFont));
auto restoreModesOnExit = wil::scope_exit([&] {
SetConsoleMode(in, originalInMode);
SetConsoleMode(out, originalOutMode);
SetConsoleCP(originalInputCP);
SetConsoleOutputCP(originalOutputCP);
SetCurrentConsoleFontEx(out, FALSE, &originalFont);
});
Log::Comment(L"Apply our modes and codepages and font.");
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(in, inputmode));
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(out, outputmode));
if (GetACP() != 932 && !Common::_isV2 && inputcp == 932)
{
Log::Comment(L"The v1 console cannot switch to Japanese unless the system ACP is 932");
Log::Comment(L"Set it in the regional control panel legacy settings and reboot first.");
VERIFY_FAIL(L"System state invalid for v1 test. Must be in Japanese (Japan) legacy locale.");
}
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleCP(inputcp));
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleOutputCP(outputcp));
auto ourFont = originalFont;
wmemcpy_s(ourFont.FaceName, ARRAYSIZE(ourFont.FaceName), wstrFont.data(), wstrFont.size());
VERIFY_WIN32_BOOL_SUCCEEDED(SetCurrentConsoleFontEx(out, FALSE, &ourFont));
const wchar_t alpha = wide[0][0];
const std::string alpha437 = char437[0];
const std::string alpha932 = char932[0];
std::string expected = inputcp == 932 ? alpha932 : alpha437;
std::wstring sendInput;
sendInput.append(&alpha, 1);
// If we're in line input, we have to send a newline and we'll get one back.
if (WI_IsFlagSet(inputmode, ENABLE_LINE_INPUT))
{
expected.append(crlf);
sendInput.append(widecrlf);
}
Log::Comment(L"send the string");
VERIFY_SUCCEEDED(_sendStringToInput(in, sendInput));
Log::Comment(L"receive the string");
std::string recvInput;
recvInput.resize(500); // excessively big
VERIFY_SUCCEEDED(_readStringFromInput(in, recvInput));
// corruption magic
// In MS Gothic, alpha is full width (2 columns)
// In Consolas, alpha is half width (1 column)
// Alpha itself is an ambiguous character, meaning the console finds the width
// by asking the font.
// Unfortunately, there's some code mixed up in the cooked read for a long time where
// the width is used as a predictor of how many bytes it will consume.
// In this specific combination of using a font where the ambiguous alpha is half width,
// the output code page doesn't support double bytes, and the input code page does...
// The result is stomped with a null as the conversion fails thinking it doesn't have enough space.
// Also, we're not maintaining this font corruption going forward. So test it for v1 only.
if (!Common::_isV2 && wstrFont == L"Consolas" && inputcp == 932 && outputcp == 437)
{
VERIFY_IS_GREATER_THAN_OR_EQUAL(recvInput.size(), 1);
VERIFY_ARE_EQUAL('\x00', recvInput[0]);
if (WI_IsFlagSet(inputmode, ENABLE_LINE_INPUT))
{
VERIFY_IS_GREATER_THAN_OR_EQUAL(recvInput.size(), 3);
VERIFY_ARE_EQUAL('\r', recvInput[1]);
VERIFY_ARE_EQUAL('\n', recvInput[2]);
}
}
// end corruption magic
else
{
VERIFY_ARE_EQUAL(expected, recvInput);
}
}
void _unifiedReadTest(std::function<void(HANDLE, ReadMode)> fn)
{
String readmode;
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"readmode", readmode), L"Get read mode");
ReadMode rm = ReadMode::Raw;
if (readmode == L"cooked")
{
rm = ReadMode::Cooked;
}
else if (readmode == L"raw")
{
rm = ReadMode::Raw;
}
else if (readmode == L"direct")
{
rm = ReadMode::Direct;
}
else
{
VERIFY_FAIL(L"Read mode not implemented on test.");
}
const auto in = GetStdInputHandle();
DWORD originalInMode = 0;
VERIFY_WIN32_BOOL_SUCCEEDED(GetConsoleMode(in, &originalInMode));
DWORD originalCodepage = GetConsoleCP();
auto restoreInModeOnExit = wil::scope_exit([&] {
SetConsoleMode(in, originalInMode);
SetConsoleCP(originalCodepage);
});
const DWORD testInMode = rm == ReadMode::Raw ? 0 : ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleMode(in, testInMode));
Log::Comment(L"Set the codepage to Japanese");
if (GetACP() != 932 && !Common::_isV2)
{
Log::Comment(L"The v1 console cannot switch to Japanese unless the system ACP is 932");
Log::Comment(L"Set it in the regional control panel legacy settings and reboot first.");
VERIFY_FAIL(L"System state invalid for v1 test. Must be in Japanese (Japan) legacy locale.");
}
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleCP(932));
Log::Comment(L"Flush out the read queue.");
VERIFY_WIN32_BOOL_SUCCEEDED(FlushConsoleInputBuffer(in));
Log::Comment(L"Write something into the read queue.");
std::wstring sendInput;
sendInput.append(wide[0]);
sendInput.append(wide[1]);
sendInput.append(wide[2]);
sendInput.append(wide[3]);
sendInput.append(L"\r\n"); // send a newline to finish the line since we're in ENABLE_LINE_INPUT mode
Log::Comment(L"send the string");
VERIFY_SUCCEEDED(_sendStringToInput(in, sendInput));
fn(in, rm);
}
std::wstring _stringToHexString(const std::string& str)
{
std::wstring ret;
for (auto& ch : str)
{
ret.append(fmt::format(L"{:#04x} ", (byte)ch));
}
return ret;
}
void _readVersusExpected(const HANDLE in, const ReadMode mode, const std::string& expected, size_t readSize)
{
// Print expected up here so if it horks, we can at least know what we asked for to debug/fix the test.
Log::Comment(fmt::format(L"Expected: {}", _stringToHexString(expected)).c_str());
std::string recvInput;
recvInput.resize(readSize);
VERIFY_SUCCEEDED(_readString(in, mode, recvInput));
Log::Comment(fmt::format(L"Actual : {}", _stringToHexString(recvInput)).c_str());
VERIFY_ARE_EQUAL(expected, recvInput);
}
// TODO tests:
// - ensure leftover bytes are lost when read off a different handle?!
void InputTests::TestReadCharByChar()
{
_unifiedReadTest([isv2 = Common::_isV2](HANDLE in, ReadMode mode) -> void {
Log::Comment(L"Read byte by byte, should leave trailing each time.");
if (!isv2)
{
std::string expectedInput;
expectedInput = char932[0][0];
if (mode != ReadMode::Direct)
{
// this is an artifact of resizing our string to the `lpNumberOfCharsRead`
// which can be longer than the buffer we gave. `ReadConsoleA` appears to
// do this either to signal there are more or as a mistake that was never
// matched up on API review.
expectedInput.append(1, '\0');
}
_readVersusExpected(in, mode, expectedInput, 1);
// TODO: CHv1 completely loses the trailing byte.
expectedInput[0] = char932[1][0];
_readVersusExpected(in, mode, expectedInput, 1);
// TODO: CHv1 completely loses the trailing byte.
expectedInput[0] = char932[2][0];
_readVersusExpected(in, mode, expectedInput, 1);
// TODO: CHv1 completely loses the trailing byte.
expectedInput[0] = char932[3][0];
_readVersusExpected(in, mode, expectedInput, 1);
// TODO: CHv1 completely loses the trailing byte.
expectedInput = crlf[0];
_readVersusExpected(in, mode, expectedInput, 1);
if (mode != ReadMode::Raw) // Raw mode will not return the \n.
{
expectedInput = crlf[1];
_readVersusExpected(in, mode, expectedInput, 1);
}
}
else
{
Log::Comment(L"Should see lead/trail alternating and then the crlf");
std::string expectedInput;
expectedInput = char932[0][0];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = char932[0][1];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = char932[1][0];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = char932[1][1];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = char932[2][0];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = char932[2][1];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = char932[3][0];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = char932[3][1];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
expectedInput = crlf[0];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
if (mode != ReadMode::Raw) // Raw mode doesn't return \n.
{
expectedInput = crlf[1];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
}
}
});
}
void InputTests::TestReadLeadTrailString()
{
_unifiedReadTest([isv2 = Common::_isV2](HANDLE in, ReadMode mode) -> void {
Log::Comment(L"Read byte by byte, should attach trailing to the remaining string.");
if (!isv2)
{
std::string expectedInput;
expectedInput = char932[0][0];
if (mode != ReadMode::Direct)
{
// this is an artifact of resizing our string to the `lpNumberOfCharsRead`
// which can be longer than the buffer we gave. `ReadConsoleA` appears to
// do this either to signal there are more or as a mistake that was never
// matched up on API review.
expectedInput.append(1, '\0');
}
_readVersusExpected(in, mode, expectedInput, 1);
Log::Comment(L"Read everything else");
// TODO: CHv1 completely loses the trailing byte.
expectedInput.clear();
if (mode != ReadMode::Raw)
{
// Direct mode can successfully return the trailing byte...
// but in v1... only when the read length is > 1 record total.
// Since this is the "string remaining" test... that's >1 record.
// (as opposed to the char-by-char test where Direct loses it just like
// Cooked and Raw do.)
if (mode == ReadMode::Direct)
{
expectedInput.append(1, char932[0][1]);
}
expectedInput.append(char932[1]);
expectedInput.append(char932[2]);
expectedInput.append(char932[3]);
expectedInput.append(1, crlf[0]);
expectedInput.append(1, crlf[1]);
}
else
{
// Raw mode messes up completely here and just returns the UTF-16 characters.
// oh and a null at the end for fun. and it loses the \n.
expectedInput.append(1, LOBYTE(wide[1][0]));
expectedInput.append(1, HIBYTE(wide[1][0]));
expectedInput.append(1, LOBYTE(wide[2][0]));
expectedInput.append(1, HIBYTE(wide[2][0]));
expectedInput.append(1, LOBYTE(wide[3][0]));
expectedInput.append(1, HIBYTE(wide[3][0]));
expectedInput.append(1, crlf[0]);
expectedInput.append(1, '\0');
}
// The test helper is authored such that direct mode will keep retrying
// to read until it gets every record requested because there's a high
// potential for other events (focus, mouse) to drop into the queue
// for random reasons.
// As such, we can read to excess on cooked/raw, but we have to read
// to the exact expected length for direct.
if (mode != ReadMode::Direct)
{
_readVersusExpected(in, mode, expectedInput, 100);
}
else
{
// We can't read too far for direct because we have to loop
// to get all the right key records and we'll end up in an infinite wait.
_readVersusExpected(in, mode, expectedInput, 9);
}
}
else
{
Log::Comment(L"Should see just lead byte.");
std::string expectedInput;
expectedInput = char932[0][0];
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
Log::Comment(L"Read everything else. Trailing byte stitched to front of results.");
expectedInput = char932[0][1];
expectedInput.append(char932[1]);
expectedInput.append(char932[2]);
expectedInput.append(char932[3]);
expectedInput.append(1, crlf[0]);
if (mode != ReadMode::Raw) // Raw mode doesn't return \n.
{
expectedInput.append(1, crlf[1]);
}
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
}
});
}
void InputTests::TestReadChangeCodepageInMiddle()
{
_unifiedReadTest([isv2 = Common::_isV2](HANDLE in, ReadMode mode) -> void {
if (!isv2)
{
Log::Comment(L"Read only part of it including leaving behind a trailing byte.");
std::string expectedInput;
expectedInput = char932[0];
// The following two only happen if you switch part way through...
expectedInput.append(char932[1].data(), 1);
// this is an artifact of resizing our string to the `lpNumberOfCharsRead`
// which can be longer than the buffer we gave. `ReadConsoleA` appears to
// do this either to signal there are more or as a mistake that was never
// matched up on API review.
if (mode != ReadMode::Direct)
{
expectedInput.append(1, '\0');
}
if (mode == ReadMode::Raw)
{
// throw on two null bytes for funsies.
expectedInput.append(1, '\0');
expectedInput.append(1, '\0');
}
_readVersusExpected(in, mode, expectedInput, 3); // two bytes of first alpha and then a lead byte of the second one.
Log::Comment(L"Set the codepage to English");
Log::Comment(L"Changing codepage should discard all partial bytes!");
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleCP(437));
Log::Comment(L"Read the rest of it and validate that it was re-encoded as English");
expectedInput.clear();
if (mode == ReadMode::Direct)
{
expectedInput.append(char437[2]);
}
expectedInput.append(char437[3]);
if (mode != ReadMode::Raw)
{
expectedInput.append(crlf);
}
else
{
// why do we get a ?... I mean why are we getting any of this weirdness.
expectedInput.append(1, '?');
}
if (mode != ReadMode::Direct)
{
_readVersusExpected(in, mode, expectedInput, 490);
}
else
{
// We can't read too far for direct because we have to loop
// to get all the right key records and we'll end up in an infinite wait.
_readVersusExpected(in, mode, expectedInput, 4);
}
}
else
{
Log::Comment(L"Read the first whole character and a lead byte of the second (3 bytes)");
std::string expectedInput;
expectedInput = char932[0];
expectedInput.append(1, char932[1][0]);
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
Log::Comment(L"Set the codepage to English");
Log::Comment(L"Changing codepage should discard all partial bytes!");
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleCP(437));
Log::Comment(L"Read everything else. Trailing byte should be gone and not stitched to front of results.");
expectedInput.clear();
expectedInput.append(char437[2]);
expectedInput.append(char437[3]);
expectedInput.append(1, crlf[0]);
if (mode != ReadMode::Raw) // Raw mode doesn't return \n.
{
expectedInput.append(1, crlf[1]);
}
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
}
});
}
void InputTests::TestReadChangeCodepageBetweenBytes()
{
_unifiedReadTest([isv2 = Common::_isV2](HANDLE in, ReadMode mode) -> void {
if (!isv2)
{
Log::Comment(L"Read only part of it including leaving behind a trailing byte.");
std::string expectedInput;
expectedInput = char932[0];
if (mode == ReadMode::Raw)
{
// throw on two null bytes for funsies.
expectedInput.append(1, '\0');
expectedInput.append(1, '\0');
}
_readVersusExpected(in, mode, expectedInput, 2); // two bytes of first alpha
Log::Comment(L"Set the codepage to English");
Log::Comment(L"Changing codepage should discard all partial bytes!");
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleCP(437));
Log::Comment(L"Read the rest of it and validate that it was re-encoded as English");
expectedInput.clear();
// TODO: I believe v2 shouldn't lose this character by switching codepages.
if (mode == ReadMode::Direct)
{
expectedInput.append(char437[1]);
}
expectedInput.append(char437[2]);
if (mode == ReadMode::Raw)
{
// an infix question mark? in the raw read? for no sensible reason?
// YEP.
expectedInput.append(1, '?');
}
expectedInput.append(char437[3]);
if (mode != ReadMode::Raw)
{
expectedInput.append(crlf);
}
if (mode != ReadMode::Direct)
{
_readVersusExpected(in, mode, expectedInput, 490);
}
else
{
// We can't read too far for direct because we have to loop
// to get all the right key records and we'll end up in an infinite wait.
_readVersusExpected(in, mode, expectedInput, 5);
}
}
else
{
Log::Comment(L"Read the first two whole characters (4 bytes)");
std::string expectedInput;
expectedInput = char932[0];
expectedInput.append(char932[1]);
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
Log::Comment(L"Set the codepage to English");
Log::Comment(L"Changing codepage should discard all partial bytes! But there shouldn't be any partials!");
VERIFY_WIN32_BOOL_SUCCEEDED(SetConsoleCP(437));
Log::Comment(L"Read everything else.");
expectedInput.clear();
expectedInput.append(char437[2]);
expectedInput.append(char437[3]);
expectedInput.append(1, crlf[0]);
if (mode != ReadMode::Raw) // Raw mode doesn't return \n.
{
expectedInput.append(1, crlf[1]);
}
_readVersusExpected(in, mode, expectedInput, expectedInput.size());
}
});
}

View File

@@ -185,7 +185,7 @@ bool Common::TestBufferSetup()
// to the default output buffer at the same time.
_hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
0 /*dwShareMode*/,
FILE_SHARE_READ | FILE_SHARE_WRITE /*dwShareMode*/, // needed to read cooked
nullptr /*lpSecurityAttributes*/,
CONSOLE_TEXTMODE_BUFFER,
nullptr /*lpReserved*/);

View File

@@ -21,6 +21,7 @@
#include <algorithm>
#include <atomic>
#include <deque>
#include <future>
#include <list>
#include <memory>
#include <map>

View File

@@ -84,7 +84,6 @@ DxEngine::DxEngine() :
_glyphCell{},
_boxDrawingEffect{},
_haveDeviceResources{ false },
_swapChainHandle{ INVALID_HANDLE_VALUE },
_swapChainDesc{ 0 },
_swapChainFrameLatencyWaitableObject{ INVALID_HANDLE_VALUE },
_recreateDeviceRequested{ false },
@@ -489,13 +488,6 @@ try
}
case SwapChainMode::ForComposition:
{
if (!_swapChainHandle)
{
RETURN_IF_FAILED(DCompositionCreateSurfaceHandle(GENERIC_ALL, nullptr, &_swapChainHandle));
}
RETURN_IF_FAILED(_dxgiFactory2.As(&_dxgiFactoryMedia));
// Use the given target size for compositions.
_swapChainDesc.Width = _displaySizePixels.width<UINT>();
_swapChainDesc.Height = _displaySizePixels.height<UINT>();
@@ -505,11 +497,10 @@ try
// It's 100% required to use scaling mode stretch for composition. There is no other choice.
_swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
RETURN_IF_FAILED(_dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(_d3dDevice.Get(),
_swapChainHandle.get(),
&_swapChainDesc,
nullptr,
&_dxgiSwapChain));
RETURN_IF_FAILED(_dxgiFactory2->CreateSwapChainForComposition(_d3dDevice.Get(),
&_swapChainDesc,
nullptr,
&_dxgiSwapChain));
break;
}
default:
@@ -851,14 +842,14 @@ try
}
CATCH_LOG()
HANDLE DxEngine::GetSwapChainHandle()
Microsoft::WRL::ComPtr<IDXGISwapChain1> DxEngine::GetSwapChain()
{
if (!_swapChainHandle)
if (_dxgiSwapChain.Get() == nullptr)
{
THROW_IF_FAILED(_CreateDeviceResources(true));
}
return _swapChainHandle.get();
return _dxgiSwapChain;
}
void DxEngine::_InvalidateRectangle(const til::rectangle& rc)

View File

@@ -63,7 +63,7 @@ namespace Microsoft::Console::Render
void SetSoftwareRendering(bool enable) noexcept;
HANDLE GetSwapChainHandle();
::Microsoft::WRL::ComPtr<IDXGISwapChain1> GetSwapChain();
// IRenderEngine Members
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override;
@@ -119,8 +119,6 @@ namespace Microsoft::Console::Render
void SetAntialiasingMode(const D2D1_TEXT_ANTIALIAS_MODE antialiasingMode) noexcept;
void SetDefaultTextBackgroundOpacity(const float opacity) noexcept;
wil::unique_handle _swapChainHandle;
void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept;
protected:
@@ -214,7 +212,6 @@ namespace Microsoft::Console::Render
::Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> _d2dBrushBackground;
::Microsoft::WRL::ComPtr<IDXGIFactory2> _dxgiFactory2;
::Microsoft::WRL::ComPtr<IDXGIFactoryMedia> _dxgiFactoryMedia;
::Microsoft::WRL::ComPtr<IDXGIDevice> _dxgiDevice;
::Microsoft::WRL::ComPtr<IDXGISurface> _dxgiSurface;

View File

@@ -21,8 +21,6 @@
#include <typeinfo>
#include <stdexcept>
#include <dcomp.h>
#include <dxgi.h>
#include <dxgi1_2.h>
#include <dxgi1_3.h>

View File

@@ -1,69 +0,0 @@
#include "pch.h"
#include "HostManager.h"
#include "HostManager.g.cpp"
#include "../../types/inc/utils.hpp"
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace ::Microsoft::Console;
namespace winrt::ScratchWinRTClient::implementation
{
HostManager::HostManager()
{
_hosts = winrt::single_threaded_observable_vector<ScratchWinRTServer::HostClass>();
}
Collections::IObservableVector<ScratchWinRTServer::HostClass> HostManager::Hosts()
{
return _hosts;
}
static void _createHostClassProcess(const winrt::guid& g)
{
auto guidStr{ Utils::GuidToString(g) };
std::wstring commandline{ fmt::format(L"ScratchWinRTServer.exe {}", guidStr) };
STARTUPINFO siOne{ 0 };
siOne.cb = sizeof(STARTUPINFOW);
wil::unique_process_information piOne;
auto succeeded = CreateProcessW(
nullptr,
commandline.data(),
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
false, // bInheritHandles
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // startingDirectory
&siOne, // lpStartupInfo
&piOne // lpProcessInformation
);
if (!succeeded)
{
printf("Failed to create host process\n");
return;
}
// Ooof this is dumb, but we need a sleep here to make the server starts.
// That's _sub par_. Maybe we could use the host's stdout to have them emit
// a byte when they're set up?
Sleep(100);
}
ScratchWinRTServer::HostClass HostManager::CreateHost()
{
// 1. Generate a GUID.
winrt::guid g{ Utils::CreateGuid() };
// 2. Spawn a Server.exe, with the guid on the commandline
_createHostClassProcess(g);
auto host = create_instance<winrt::ScratchWinRTServer::HostClass>(g, CLSCTX_LOCAL_SERVER);
THROW_IF_NULL_ALLOC(host);
_hosts.Append(host);
return host;
}
}

View File

@@ -1,65 +0,0 @@
#pragma once
#include "HostManager.g.h"
// {50dba6cd-4ddb-4b12-8363-5e06f5d0082c}
static constexpr GUID HostManager_clsid{
0x50dba6cd,
0x4ddb,
0x4b12,
{ 0x83, 0x63, 0x5e, 0x06, 0xf5, 0xd0, 0x08, 0x2c }
};
namespace winrt::ScratchWinRTClient::implementation
{
struct HostManager : public HostManagerT<HostManager>
{
HostManager();
Windows::Foundation::Collections::IObservableVector<ScratchWinRTServer::HostClass> Hosts();
ScratchWinRTServer::HostClass CreateHost();
private:
Windows::Foundation::Collections::IObservableVector<ScratchWinRTServer::HostClass> _hosts{ nullptr };
};
}
namespace winrt::ScratchWinRTClient::factory_implementation
{
struct HostManager : HostManagerT<HostManager, implementation::HostManager>
{
};
}
// I bet all this could be a macro.
// I MORE be that this is all done by the factory_implementation stuff, isn't it...
struct HostManagerFactory : winrt::implements<HostManagerFactory, IClassFactory>
{
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
{
*result = nullptr;
if (outer)
{
return CLASS_E_NOAGGREGATION;
}
return winrt::make<winrt::ScratchWinRTClient::implementation::HostManager>().as(iid, result);
}
HRESULT __stdcall LockServer(BOOL) noexcept final
{
return S_OK;
}
static void RegisterHostManager()
{
DWORD registrationHostManager{};
winrt::check_hresult(CoRegisterClassObject(HostManager_clsid,
winrt::make<HostManagerFactory>().get(),
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&registrationHostManager));
printf("registrationHostManager:%d\n", registrationHostManager);
}
};

View File

@@ -1,12 +0,0 @@
namespace ScratchWinRTClient
{
[default_interface] runtimeclass HostManager // : IScratchInterface
{
HostManager();
Windows.Foundation.Collections.IObservableVector<ScratchWinRTServer.HostClass> Hosts { get; };
ScratchWinRTServer.HostClass CreateHost();
};
}

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>

View File

@@ -1,44 +0,0 @@
To checkout the solution, and the branch I'm on, do the following:
```cmd
git clone https://github.com/microsoft/terminal.git
cd terminal
git checkout dev/migrie/oop-mixed-elevation-1
git submodule update --init --recursive
```
Then, to build the scratch projects I'm working with, do the following (in `cmd`):
_You may need to open the solution in VS once first, to make sure it installs all the dependencies!_
```cmd
.\tools\razzle.cmd
nuget restore
cd src\tools\ScratchWinRTClient
bx
```
(after the first build, `bx` will just rebuild the `ScratchWinRTClient` and `ScratchWinRTServer` projects.)
That should build both `ScratchWinRTClient` and `ScratchWinRTServer`. However, the client needs the server exe to live next to it, so you'll need to do the following (from the root of the solution)
```cmd
copy /y bin\x64\Debug\ScratchWinRTServer\ScratchWinRTServer.exe bin\x64\Debug\ScratchWinRTClient\
```
whenever the server's code changes.
Then, in one terminal window, run (again, from the root of the solution)
```cmd
bin\x64\Debug\ScratchWinRTClient\ScratchWinRTClient.exe
```
That'll print out a list of "Hosts" and their GUIDs. Copy one of those GUIDs, including braces. Then, in an elevated window, run:
```cmd
bin\x64\Debug\ScratchWinRTClient\ScratchWinRTClient.exe {the guid}
```
That'll hit the code in `createExistingObjectApp`, where we try to de-elevate

View File

@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{06382349-d62a-4c7d-a7d3-9ca817eae092}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ScratchWinRTClient</RootNamespace>
<ProjectName>ScratchWinRTClient</ProjectName>
<TargetName>ScratchWinRTClient</TargetName>
<ConfigurationType>Application</ConfigurationType>
<OpenConsoleUniversalApp>false</OpenConsoleUniversalApp>
<ApplicationType>Windows Store</ApplicationType>
<TargetPlatformIdentifier>Windows</TargetPlatformIdentifier>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<PropertyGroup>
<GenerateManifest>true</GenerateManifest>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<!-- Source Files -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="HostManager.h">
<DependentUpon>HostManager.idl</DependentUpon>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="HostManager.cpp">
<DependentUpon>HostManager.idl</DependentUpon>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Midl Include="HostManager.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<!-- Dependencies -->
<ItemGroup>
<ProjectReference Include="..\ScratchWinRTServer\ScratchWinRTServer.vcxproj">
<Project>{d46d9547-f085-4645-b8f7-e8cd21559ab4}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)src\types\lib\types.vcxproj" />
</ItemGroup>
<!--
This ItemGroup and the Globals PropertyGroup below it are required in order
to enable F5 debugging for the unpackaged application
-->
<ItemGroup>
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_general.xml" />
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_local_windows.xml" />
</ItemGroup>
<PropertyGroup Label="Globals">
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- These have to come after post.props because the Cpp common targets will inexplicably overwrite them. -->
<ItemDefinitionGroup>
<ClCompile>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<!-- <Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" /> -->
</Project>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Text Include="readme.txt" />
</ItemGroup>
</Project>

View File

@@ -1,376 +0,0 @@
#include "pch.h"
#include <conio.h>
#include "HostManager.h"
#include <winrt/ScratchWinRTServer.h>
#include "../../types/inc/utils.hpp"
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace ::Microsoft::Console;
void createExistingObjectApp(int /*argc*/, char** argv)
{
winrt::guid guidFromCmdline{};
std::string guidString{ argv[1] };
auto canConvert = guidString.length() == 38 && guidString.front() == '{' && guidString.back() == '}';
if (canConvert)
{
std::wstring wideGuidStr{ til::u8u16(guidString) };
printf("\x1b[90mCLIENT: Found GUID:%ls\x1b[m\n", wideGuidStr.c_str());
GUID result{};
THROW_IF_FAILED(IIDFromString(wideGuidStr.c_str(), &result));
guidFromCmdline = result;
}
if (guidFromCmdline == winrt::guid{})
{
printf("client did not recieve GUID, early returning.");
return; // -1;
}
winrt::ScratchWinRTServer::HostClass host{ nullptr };
{
HANDLE hProcessToken;
auto processTokenCleanup = wil::scope_exit([&] { CloseHandle(hProcessToken); });
// Open a handle to the access token for the
// calling process that is the currently login access token
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hProcessToken))
{
printf("OpenProcessToken()-Getting the handle to access token failed, error %u\n", GetLastError());
}
else
{
printf("OpenProcessToken()-Got the handle to access token!\n");
}
{
TOKEN_ELEVATION tokenElevation{ 0 };
DWORD neededTokenInformationLength = 0;
if (GetTokenInformation(hProcessToken,
TOKEN_INFORMATION_CLASS::TokenElevation,
&tokenElevation,
sizeof(tokenElevation),
&neededTokenInformationLength))
{
printf("GetTokenInformation(TokenElevation) succeeded\n");
printf("Token is elevated? - %s\n", (tokenElevation.TokenIsElevated != 0 ? "true" : "false"));
}
else
{
auto gle = GetLastError();
printf("GetTokenInformation(TokenElevation) failed: %d\n", gle);
}
}
TOKEN_LINKED_TOKEN tokenLinkedToken = {};
DWORD neededTokenInformationLength = 0;
if (!GetTokenInformation(hProcessToken,
TokenLinkedToken,
&tokenLinkedToken,
sizeof(tokenLinkedToken),
&neededTokenInformationLength))
{
auto gle = GetLastError();
printf("GetTokenInformation(TokenLinkedToken) failed: %d\n", gle);
return;
}
else
{
printf("Got the linked token for this process\n");
}
auto tokenLinkedTokenCleanup = wil::scope_exit([&] { CloseHandle(tokenLinkedToken.LinkedToken); });
// THIS IS THE DAMNDEST THING
//
// IF YOU DO THIS, THE PROCESS WILL JUST STRAIGHT UP DIE ON THE create_instance CALL
if (SetThreadToken(nullptr, tokenLinkedToken.LinkedToken))
{
printf("SetThreadToken() succeeded\n");
}
else
{
printf("SetThreadToken failed %x\n", GetLastError());
}
// // Lets the calling process impersonate the security context of a
// // logged-on user. UNFORTUNATELY, this did not work for me.
// // * ImpersonateLoggedOnUser(tokenLinkedToken.LinkedToken) does the same
// // thing as SetThreadToken(LinkedToken), it crashes when trying to
// // create_instance.
// // * ImpersonateLoggedOnUser(hProcessToken) does seemingly nothing at
// // all - we get an "Error: Class not registered"
//
// auto revert = wil::scope_exit([&]() {
// // Terminates the impersonation of a client.
// if (RevertToSelf())
// {
// printf("Impersonation was terminated.\n");
// }
// else
// {
// auto gle = GetLastError();
// printf("RevertToSelf failed, %d\n", gle);
// }
// });
// // if (ImpersonateLoggedOnUser(hProcessToken))
// if (ImpersonateLoggedOnUser(tokenLinkedToken.LinkedToken))
// {
// printf("ImpersonateLoggedOnUser() is OK.\n");
// }
// else
// {
// printf("ImpersonateLoggedOnUser() failed, error %u.\n", GetLastError());
// exit(-1);
// }
printf("Calling create_instance...\n");
host = create_instance<winrt::ScratchWinRTServer::HostClass>(guidFromCmdline,
// CLSCTX_LOCAL_SERVER);
CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING);
printf("Done\n");
}
if (!host)
{
printf("Could not get the existing HostClass\n");
return;
}
else
{
printf("Got the existing HostClass\n");
}
// The DoCount could be anything, depending on which of the hosts we're creating.
printf("DoCount: %d (Expected: ?)\n",
host.DoCount());
}
void createHostClassProcess(const winrt::guid& g)
{
auto guidStr{ Utils::GuidToString(g) };
std::wstring commandline{ fmt::format(L"ScratchWinRTServer.exe {}", guidStr) };
STARTUPINFO siOne{ 0 };
siOne.cb = sizeof(STARTUPINFOW);
wil::unique_process_information piOne;
auto succeeded = CreateProcessW(
nullptr,
commandline.data(),
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
false, // bInheritHandles
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // startingDirectory
&siOne, // lpStartupInfo
&piOne // lpProcessInformation
);
if (!succeeded)
{
printf("Failed to create first host process\n");
return;
}
// Ooof this is dumb, but we need a sleep here to make the server starts.
// That's _sub par_. Maybe we could use the host's stdout to have them emit
// a byte when they're set up?
Sleep(1000);
}
void scratchApp()
{
printf("scratchApp\n");
// 1. Generate a GUID.
// 2. Spawn a Server.exe, with the guid on the commandline
// 3. Make an instance of that GUID, as a HostClass
// 4. Call HostClass::DoTheThing, and get the count with HostClass::DoCount [1]
// 5. Make another instance of HostClass, and get the count with HostClass::DoCount. It should be the same. [1, 1]
// 6. On the second HostClass, call DoTheThing. Verify that both instances have the same DoCount. [2. 2]
// 7. Create a second Server.exe, and create a Third HostClass, using that GUID.
// 8. Call DoTheThing on the third, and verify the counts of all three instances. [2, 2, 1]
// 9. QUESTION: Does releasing the first instance leave the first object alive, since the second instance still points at it?
// 1. Generate a GUID.
winrt::guid firstGuid{ Utils::CreateGuid() };
// 2. Spawn a Server.exe, with the guid on the commandline
createHostClassProcess(firstGuid);
// 3. Make an instance of that GUID, as a HostClass
printf("Trying to directly create a HostClass...\n");
auto firstHost = create_instance<winrt::ScratchWinRTServer::HostClass>(firstGuid, CLSCTX_LOCAL_SERVER);
if (!firstHost)
{
printf("Could not get the first HostClass\n");
return;
}
printf("DoCount: %d (Expected: 0)\n",
firstHost.DoCount());
// 4. Call HostClass::DoTheThing, and get the count with HostClass::DoCount [1]
firstHost.DoTheThing();
printf("DoCount: %d (Expected: 1)\n",
firstHost.DoCount());
// 5. Make another instance of HostClass, and get the count with HostClass::DoCount. It should be the same. [1, 1]
auto secondHost = create_instance<winrt::ScratchWinRTServer::HostClass>(firstGuid, CLSCTX_LOCAL_SERVER);
if (!secondHost)
{
printf("Could not get the second HostClass\n");
return;
}
printf("DoCount: [%d, %d] (Expected: [1, 1])\n",
firstHost.DoCount(),
secondHost.DoCount());
// 6. On the second HostClass, call DoTheThing. Verify that both instances have the same DoCount. [2. 2]
secondHost.DoTheThing();
printf("DoCount: [%d, %d] (Expected: [2, 2])\n",
firstHost.DoCount(),
secondHost.DoCount());
// 7. Create a second Server.exe, and create a Third HostClass, using that GUID.
winrt::guid secondGuid{ Utils::CreateGuid() };
createHostClassProcess(secondGuid);
auto thirdHost = create_instance<winrt::ScratchWinRTServer::HostClass>(secondGuid, CLSCTX_LOCAL_SERVER);
if (!thirdHost)
{
printf("Could not get the third HostClass\n");
return;
}
printf("DoCount: [%d, %d, %d] (Expected: [2, 2, 0])\n",
firstHost.DoCount(),
secondHost.DoCount(),
thirdHost.DoCount());
// 8. Call DoTheThing on the third, and verify the counts of all three instances. [2, 2, 1]
thirdHost.DoTheThing();
printf("DoCount: [%d, %d, %d] (Expected: [2, 2, 1])\n",
firstHost.DoCount(),
secondHost.DoCount(),
thirdHost.DoCount());
}
static void printHosts(const ScratchWinRTClient::HostManager& manager)
{
int index = 0;
for (const auto& h : manager.Hosts())
{
auto guidStr{ Utils::GuidToString(h.Id()) };
printf("Host[%d]: DoCount=%d %ls\n", index, h.DoCount(), guidStr.c_str());
index++;
}
if (index == 0)
{
printf("<No hosts>\n");
}
}
void managerApp()
{
ScratchWinRTClient::HostManager manager;
printHosts(manager);
printf("Create host 0:\n");
auto host0 = manager.CreateHost();
printHosts(manager);
printf("Create host 1:\n");
auto host1 = manager.CreateHost();
host1.DoTheThing();
printHosts(manager);
printf("Create host 2:\n");
auto host2 = manager.CreateHost();
host2.DoTheThing();
host2.DoTheThing();
printHosts(manager);
printf("Create host 3:\n");
auto host3 = manager.CreateHost();
host3.DoTheThing();
host3.DoTheThing();
host3.DoTheThing();
printHosts(manager);
printf("increment host 0:\n");
host0.DoTheThing();
host0.DoTheThing();
host0.DoTheThing();
host0.DoTheThing();
printHosts(manager);
bool exitRequested = false;
while (!exitRequested)
{
printf("-----------------------------\n");
printf("input a command (l, i, c, q): ");
const auto ch = _getch();
printf("\n");
if (ch == 'l')
{
printHosts(manager);
}
else if (ch == 'i')
{
printf("input a host to increment: ");
const auto ch2 = _getch();
if (ch2 >= '0' && ch2 <= '9')
{
uint32_t index = ((int)(ch2)) - ((int)('0'));
if (index < manager.Hosts().Size())
{
manager.Hosts().GetAt(index).DoTheThing();
printHosts(manager);
}
}
}
else if (ch == 'c')
{
printf("Creating a new host\n");
manager.CreateHost();
printHosts(manager);
}
else if (ch == 'q')
{
exitRequested = true;
}
}
}
int main(int argc, char** argv)
{
auto hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
init_apartment();
try
{
// If a GUID was passed on the commandline, then try to instead make an instance of that class.
if (argc > 1)
{
createExistingObjectApp(argc, argv);
}
else
{
managerApp();
}
}
catch (hresult_error const& e)
{
printf("Error: %ls\n", e.message().c_str());
}
catch (...)
{
printf("Some other exception happened\n");
LOG_CAUGHT_EXCEPTION();
}
printf("Exiting client");
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.200609.3" targetFramework="native" />
</packages>

View File

@@ -1 +0,0 @@
#include "pch.h"

View File

@@ -1,28 +0,0 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#define BLOCK_TIL
#include <wil/cppwinrt.h>
#undef max
#undef min
#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 <Unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Media.h>
#include <windows.ui.xaml.media.dxinterop.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"

View File

@@ -1,51 +0,0 @@
#include "pch.h"
#include <argb.h>
#include <DefaultSettings.h>
#include "HostClass.h"
#include "HostClass.g.cpp"
// #include <wrl.h>
extern std::mutex m;
extern std::condition_variable cv;
extern bool dtored;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Input;
using namespace winrt::Windows::UI::Xaml::Automation::Peers;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::ViewManagement;
using namespace winrt::Windows::UI::Input;
using namespace winrt::Windows::System;
// using namespace winrt::Microsoft::Terminal::Settings;
// using namespace winrt::Windows::ApplicationModel::DataTransfer;
namespace winrt::ScratchWinRTServer::implementation
{
HostClass::HostClass(const winrt::guid& g) :
_id{ g }
{
printf("HostClass()\n");
}
HostClass::~HostClass()
{
printf("~HostClass()\n");
std::unique_lock<std::mutex> lk(m);
dtored = true;
cv.notify_one();
}
int HostClass::DoCount()
{
return _DoCount;
}
void HostClass::DoTheThing()
{
_DoCount++;
}
winrt::guid HostClass::Id()
{
return _id;
}
}

View File

@@ -1,27 +0,0 @@
#pragma once
#include "HostClass.g.h"
namespace winrt::ScratchWinRTServer::implementation
{
struct HostClass : HostClassT<HostClass>
{
HostClass(const winrt::guid& g);
~HostClass();
void DoTheThing();
int DoCount();
winrt::guid Id();
private:
int _DoCount{ 0 };
winrt::guid _id;
};
}
namespace winrt::ScratchWinRTServer::factory_implementation
{
struct HostClass : HostClassT<HostClass, implementation::HostClass>
{
};
}

View File

@@ -1,11 +0,0 @@
namespace ScratchWinRTServer
{
[default_interface] runtimeclass HostClass {
HostClass(Guid g);
void DoTheThing();
Int32 DoCount { get; };
Guid Id { get; };
};
}

View File

@@ -1,6 +0,0 @@
#pragma once
struct __declspec(uuid("28578d33-c090-4279-9669-dbeea3f24bb0")) IMyComInterface : ::IUnknown
{
virtual HRESULT __stdcall Call() = 0;
};

View File

@@ -1,9 +0,0 @@
// [
// uuid("BB64926F-1A4D-470D-BB8A-3D2CC4B035E4"),
// object,
// local
// ] interface IMyComInterface : IUnknown
// {
// HRESULT MyMethod();
// };

View File

@@ -1,11 +0,0 @@
namespace ScratchWinRTServer
{
// [default_interface]
interface IScratchInterface
{
String DoTheThing();
};
}

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>

View File

@@ -1,11 +0,0 @@
#include "pch.h"
#include "ScratchClass.h"
#include "ScratchClass.g.cpp"
namespace winrt::ScratchWinRTServer::implementation
{
ScratchClass::ScratchClass()
{
}
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include "ScratchClass.g.h"
namespace winrt::ScratchWinRTServer::implementation
{
struct ScratchClass : public ScratchClassT<ScratchClass>
{
ScratchClass();
hstring DoTheThing()
{
return L"Hello there";
}
};
}
namespace winrt::ScratchWinRTServer::factory_implementation
{
struct ScratchClass : ScratchClassT<ScratchClass, implementation::ScratchClass>
{
};
}

View File

@@ -1,15 +0,0 @@
namespace ScratchWinRTServer
{
[default_interface] runtimeclass ScratchClass // : IScratchInterface
{
ScratchClass();
String DoTheThing();
// Adding a Windows.UI.Xaml element will crash the server when the
// server tries to instantiate the element without a XAML host.
//
// Windows.UI.Xaml.Controls.Button MyButton { get; };
};
}

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 1903 -->
<!-- See https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/xaml-islands -->
<!-- "maxversiontested" is CASE SENSITIVE. Do not change this.-->
<maxversiontested Id="10.0.18362.0"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
<file name="ScratchWinRTServer.exe" hashalg="SHA1">
<activatableClass name="ScratchWinRTServer.ScratchClass" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1"></activatableClass>
</file>
</assembly>

View File

@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{d46d9547-f085-4645-b8f7-e8cd21559ab4}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ScratchWinRTServer</RootNamespace>
<ProjectName>ScratchWinRTServer</ProjectName>
<TargetName>ScratchWinRTServer</TargetName>
<ConfigurationType>Application</ConfigurationType>
<OpenConsoleUniversalApp>false</OpenConsoleUniversalApp>
<ApplicationType>Windows Store</ApplicationType>
<TargetPlatformIdentifier>Windows</TargetPlatformIdentifier>
</PropertyGroup>
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<PropertyGroup>
<GenerateManifest>true</GenerateManifest>
<EmbedManifest>true</EmbedManifest>
</PropertyGroup>
<!-- Source Files -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="ScratchClass.h" />
<ClInclude Include="HostClass.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ScratchClass.cpp">
<DependentUpon>ScratchClass.idl</DependentUpon>
</ClCompile>
<ClCompile Include="HostClass.cpp">
<DependentUpon>HostClass.idl</DependentUpon>
</ClCompile>
<ClCompile Include="main.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Midl Include="IScratchInterface.idl" />
<Midl Include="ScratchClass.idl" />
<Midl Include="HostClass.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<!-- Dependencies -->
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src\types\lib\types.vcxproj" />
</ItemGroup>
<!--
This ItemGroup and the Globals PropertyGroup below it are required in order
to enable F5 debugging for the unpackaged application
-->
<ItemGroup>
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_general.xml" />
<PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_local_windows.xml" />
</ItemGroup>
<PropertyGroup Label="Globals">
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
<!-- These have to come after post.props because the Cpp common targets will inexplicably overwrite them. -->
<ItemDefinitionGroup>
<ClCompile>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<!-- <Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" /> -->
</Project>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="PropertySheet.props" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Text Include="readme.txt" />
</ItemGroup>
</Project>

View File

@@ -1,107 +0,0 @@
#include "pch.h"
#include <winrt/ScratchWinRTServer.h>
#include "ScratchClass.h"
#include "HostClass.h"
using namespace winrt;
// using namespace winrt::Windows::Foundation;
std::mutex m;
std::condition_variable cv;
bool dtored = false;
winrt::weak_ref<ScratchWinRTServer::implementation::HostClass> g_weak{ nullptr };
struct HostClassFactory : implements<HostClassFactory, IClassFactory>
{
HostClassFactory(winrt::guid g) :
_guid{ g } {};
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
{
*result = nullptr;
if (outer)
{
return CLASS_E_NOAGGREGATION;
}
if (!g_weak)
{
auto strong = make_self<ScratchWinRTServer::implementation::HostClass>(_guid);
g_weak = (*strong).get_weak();
return strong.as(iid, result);
}
else
{
auto strong = g_weak.get();
return strong.as(iid, result);
}
}
HRESULT __stdcall LockServer(BOOL) noexcept final
{
return S_OK;
}
private:
winrt::guid _guid;
};
int main(int argc, char** argv)
{
auto hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
printf("\x1b[90mSERVER: args:[");
if (argc > 0)
{
for (int i = 0; i < argc; i++)
{
printf("%s,", argv[i]);
}
}
printf("]\x1b[m\n");
winrt::guid guidFromCmdline{};
if (argc > 1)
{
std::string guidString{ argv[1] };
// Obviously this isn't right but it's good enough
auto canConvert = guidString.length() == 38 && guidString.front() == '{' && guidString.back() == '}';
if (canConvert)
{
std::wstring wideGuidStr{ til::u8u16(guidString) };
printf("\x1b[90mSERVER: Found GUID:%ls\x1b[m\n", wideGuidStr.c_str());
GUID result{};
THROW_IF_FAILED(IIDFromString(wideGuidStr.c_str(), &result));
guidFromCmdline = result;
}
}
if (guidFromCmdline == winrt::guid{})
{
printf("did not recieve GUID, early returning.");
return -1;
}
init_apartment();
DWORD registrationHostClass{};
check_hresult(CoRegisterClassObject(guidFromCmdline,
make<HostClassFactory>(guidFromCmdline).get(),
CLSCTX_LOCAL_SERVER,
// CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_AAA,
// CLSCTX_LOCAL_SERVER | CLSCTX_ACTIVATE_AAA_AS_IU,
// CLSCTX_LOCAL_SERVER | CLSCTX_FROM_DEFAULT_CONTEXT,
// CLSCTX_LOCAL_SERVER | CLSCTX_ENABLE_CLOAKING,
REGCLS_MULTIPLEUSE,
&registrationHostClass));
printf("%d\n", registrationHostClass);
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] { return dtored; });
printf("\x1b[90mSERVER: exiting %s\n\x1b[m", argv[1]);
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.200609.3" targetFramework="native" />
</packages>

View File

@@ -1 +0,0 @@
#include "pch.h"

View File

@@ -1,43 +0,0 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#define BLOCK_TIL
#include <wil/cppwinrt.h>
#undef max
#undef min
#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 <Unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Media.h>
#include <windows.ui.xaml.media.dxinterop.h>
// Manually include til after we include Windows.Foundation to give it winrt superpowers
#include "til.h"
#include <dxgi.h>
#include <dxgi1_2.h>
#include <dxgi1_3.h>
#include <d3d11.h>
#include <d2d1.h>
#include <d2d1_1.h>
#include <d2d1_2.h>
#include <d2d1_3.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <dwrite_1.h>
#include <dwrite_2.h>
#include <dwrite_3.h>

View File

@@ -1,30 +0,0 @@
========================================================================
C++/WinRT ScratchWinRTServer Project Overview
========================================================================
This project demonstrates how to get started consuming Windows Runtime
classes directly from standard C++, using platform projection headers
generated from Windows SDK metadata files.
Steps to generate and consume SDK platform projection:
1. Build project initally to generate platform projection headers into
your Generated Files folder.
2. Include a projection namespace header in your pch.h, such as
<winrt/Windows.Foundation.h>.
3. Consume winrt namespace and any Windows Runtime namespaces, such as
winrt::Windows::Foundation, from source code.
4. Initialize apartment via init_apartment() and consume winrt classes.
Steps to generate and consume a projection from third party metadata:
1. Add a WinMD reference by right-clicking the References project node
and selecting "Add Reference...". In the Add References dialog,
browse to the component WinMD you want to consume and add it.
2. Build the project once to generate projection headers for the
referenced WinMD file under the "Generated Files" subfolder.
3. As above, include projection headers in pch or source code
to consume projected Windows Runtime classes.
========================================================================
Learn more about C++/WinRT here:
http://aka.ms/cppwinrt/
========================================================================