mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-07 06:39:44 +00:00
Compare commits
13 Commits
dev/migrie
...
dev/miniks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb72c7fd3b | ||
|
|
20250c233d | ||
|
|
39023426ff | ||
|
|
9ea43bace0 | ||
|
|
2b9f6388cf | ||
|
|
c340ab5e7b | ||
|
|
42e198e967 | ||
|
|
a6816df2b5 | ||
|
|
3aee3992ba | ||
|
|
1b3a5e671b | ||
|
|
18b91c20ef | ||
|
|
3b7544770b | ||
|
|
93b95420ac |
12
.github/ISSUE_TEMPLATE/config.yml
vendored
12
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -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
57
.github/workflows/linter.yml
vendored
Normal 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
|
||||
105
OpenConsole.sln
105
OpenConsole.sln
@@ -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}
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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="">
|
||||
</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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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<>);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
18
src/cascadia/TerminalApp/Tab.idl
Normal file
18
src/cascadia/TerminalApp/Tab.idl
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 ======================== -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "TabBase.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TerminalTab : TabBase
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"showTabsInTitlebar": true,
|
||||
"showTerminalTitleInTitlebar": true,
|
||||
"tabWidthMode": "equal",
|
||||
"useTabSwitcher": "mru",
|
||||
"useTabSwitcher": true,
|
||||
|
||||
// Miscellaneous
|
||||
"confirmCloseAllTabs": true,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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*/);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <future>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
#include <typeinfo>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <dcomp.h>
|
||||
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <dxgi1_3.h>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
®istrationHostManager));
|
||||
printf("registrationHostManager:%d\n", registrationHostManager);
|
||||
}
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
|
||||
namespace ScratchWinRTClient
|
||||
{
|
||||
[default_interface] runtimeclass HostManager // : IScratchInterface
|
||||
{
|
||||
HostManager();
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<ScratchWinRTServer.HostClass> Hosts { get; };
|
||||
ScratchWinRTServer.HostClass CreateHost();
|
||||
};
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1 +0,0 @@
|
||||
#include "pch.h"
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
|
||||
namespace ScratchWinRTServer
|
||||
{
|
||||
[default_interface] runtimeclass HostClass {
|
||||
HostClass(Guid g);
|
||||
void DoTheThing();
|
||||
Int32 DoCount { get; };
|
||||
Guid Id { get; };
|
||||
};
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
struct __declspec(uuid("28578d33-c090-4279-9669-dbeea3f24bb0")) IMyComInterface : ::IUnknown
|
||||
{
|
||||
virtual HRESULT __stdcall Call() = 0;
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
// [
|
||||
// uuid("BB64926F-1A4D-470D-BB8A-3D2CC4B035E4"),
|
||||
// object,
|
||||
// local
|
||||
// ] interface IMyComInterface : IUnknown
|
||||
// {
|
||||
// HRESULT MyMethod();
|
||||
// };
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
|
||||
namespace ScratchWinRTServer
|
||||
{
|
||||
// [default_interface]
|
||||
interface IScratchInterface
|
||||
{
|
||||
String DoTheThing();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "ScratchClass.h"
|
||||
|
||||
#include "ScratchClass.g.cpp"
|
||||
namespace winrt::ScratchWinRTServer::implementation
|
||||
{
|
||||
ScratchClass::ScratchClass()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -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; };
|
||||
};
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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,
|
||||
®istrationHostClass));
|
||||
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]);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1 +0,0 @@
|
||||
#include "pch.h"
|
||||
@@ -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>
|
||||
@@ -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/
|
||||
========================================================================
|
||||
Reference in New Issue
Block a user