mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-20 22:06:56 +00:00
Compare commits
53 Commits
dev/cazamo
...
v1.14.2281
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a277b56f6a | ||
|
|
2d00432dab | ||
|
|
793e4170a6 | ||
|
|
3bf217419e | ||
|
|
13236a2d91 | ||
|
|
4948f25d40 | ||
|
|
5cc4805439 | ||
|
|
ac9d69790f | ||
|
|
1642360c4c | ||
|
|
1a56bc5101 | ||
|
|
ec6dc8dd67 | ||
|
|
f1b75e5557 | ||
|
|
e110c5b639 | ||
|
|
a47bb0d0dc | ||
|
|
d7d15db93b | ||
|
|
e5239f8931 | ||
|
|
970d2d0514 | ||
|
|
df6c43fc69 | ||
|
|
2844209834 | ||
|
|
f40aa7d957 | ||
|
|
741ea4d96a | ||
|
|
fbfb83bb97 | ||
|
|
2687865790 | ||
|
|
6dcc1762ee | ||
|
|
102f612689 | ||
|
|
199992a867 | ||
|
|
efa02f35e2 | ||
|
|
ddf46f2727 | ||
|
|
0bd085d11b | ||
|
|
78cda41828 | ||
|
|
9165c00d49 | ||
|
|
93b5dff5f8 | ||
|
|
31757a44ee | ||
|
|
a2244246b5 | ||
|
|
988f64ab15 | ||
|
|
b796060c55 | ||
|
|
7506a3ab2d | ||
|
|
997b4acce5 | ||
|
|
287db8a483 | ||
|
|
07f7717112 | ||
|
|
39c71d6421 | ||
|
|
c0e6c259fc | ||
|
|
6d636056a0 | ||
|
|
20a6c7078b | ||
|
|
eec6a882d6 | ||
|
|
bc9e43a19d | ||
|
|
858359e62e | ||
|
|
5fbd5392e7 | ||
|
|
28284e824f | ||
|
|
61e6927f34 | ||
|
|
95693e9079 | ||
|
|
eb40b51b79 | ||
|
|
521d968a75 |
1
.github/actions/spelling/allow/allow.txt
vendored
1
.github/actions/spelling/allow/allow.txt
vendored
@@ -44,6 +44,7 @@ inlined
|
||||
It'd
|
||||
kje
|
||||
libfuzzer
|
||||
libuv
|
||||
liga
|
||||
lje
|
||||
Llast
|
||||
|
||||
2
.github/actions/spelling/allow/apis.txt
vendored
2
.github/actions/spelling/allow/apis.txt
vendored
@@ -30,7 +30,6 @@ DERR
|
||||
dlldata
|
||||
DONTADDTORECENT
|
||||
DWORDLONG
|
||||
DWMWA
|
||||
endfor
|
||||
enumset
|
||||
environstrings
|
||||
@@ -155,7 +154,6 @@ serializer
|
||||
SETVERSION
|
||||
SHELLEXECUTEINFOW
|
||||
shobjidl
|
||||
SHOWDEFAULT
|
||||
SHOWHIDE
|
||||
SHOWMINIMIZED
|
||||
SHOWTIP
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
<package id="Microsoft.Taef" version="10.60.210621002" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.PGO-Helpers.Cpp" version="0.2.34" targetFramework="native" />
|
||||
<!-- This cannot be included in another project that depends on XAML (as it would be a duplicate package ID) -->
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.0" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3" targetFramework="native" />
|
||||
</packages>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Optional, defaults to main. Name of the branch which will be used for calculating branch point. -->
|
||||
<PGOBranch>main</PGOBranch>
|
||||
<PGOBranch>release-1.14</PGOBranch>
|
||||
|
||||
<!-- Mandatory. Name of the NuGet package which will contain PGO databases for consumption by build system. -->
|
||||
<PGOPackageName>Microsoft.Internal.Windows.Terminal.PGODatabase</PGOPackageName>
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
If ($Arch -Eq "x86") { $Arch = "Win32" }
|
||||
|
||||
Write-Host "##vso[task.setvariable variable=RationalizedBuildPlatform]${Arch}"
|
||||
- steps: restore-nuget-steps.yml
|
||||
- template: restore-nuget-steps.yml
|
||||
- task: UniversalPackages@0
|
||||
displayName: Download terminal-internal Universal Package
|
||||
inputs:
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
This version should be tracked in all project packages.config files for projects that depend on Xaml.
|
||||
-->
|
||||
<TerminalMUXVersion>2.7.2-prerelease.220406002</TerminalMUXVersion>
|
||||
<TerminalMUXVersion>2.7.3-prerelease.220816001</TerminalMUXVersion>
|
||||
<!--
|
||||
For the Windows 11-specific build, we're targeting the public version of Microsoft.UI.Xaml.
|
||||
This version emits a package dependency instead of embedding the dependency in our own package.
|
||||
|
||||
This version should be tracked in build/packages.config.
|
||||
-->
|
||||
<TerminalMUXVersion Condition="'$(TerminalTargetWindowsVersion)'=='Win11'">2.7.1</TerminalMUXVersion>
|
||||
<TerminalMUXVersion Condition="'$(TerminalTargetWindowsVersion)'=='Win11'">2.7.3</TerminalMUXVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />
|
||||
<package id="Microsoft.Internal.Windows.Terminal.ThemeHelpers" version="0.6.220404001" targetFramework="native" />
|
||||
<package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="2.3.2262" targetFramework="native" developmentDependency="true" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.2-prerelease.220406002" targetFramework="native" />
|
||||
<package id="Microsoft.UI.Xaml" version="2.7.3-prerelease.220816001" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.220201.1" targetFramework="native" developmentDependency="true" />
|
||||
|
||||
<!-- Managed packages -->
|
||||
<package id="Appium.WebDriver" version="3.0.0.2" targetFramework="net45" />
|
||||
<package id="Castle.Core" version="4.1.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net45" />
|
||||
<package id="Selenium.Support" version="3.5.0" targetFramework="net45" />
|
||||
<package id="Selenium.WebDriver" version="3.5.0" targetFramework="net45" />
|
||||
</packages>
|
||||
|
||||
@@ -1708,6 +1708,11 @@
|
||||
"description": "Force the terminal to use the legacy input encoding. Certain keys in some applications may stop working when enabling this setting.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental.useBackgroundImageForWindow": {
|
||||
"default": false,
|
||||
"description": "When set to true, the background image for the currently focused profile is expanded to encompass the entire window, beneath other panes.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"initialCols": {
|
||||
"default": 120,
|
||||
"description": "The number of columns displayed in the window upon first load. If \"launchMode\" is set to \"maximized\" (or \"maximizedFocus\"), this property is ignored.",
|
||||
@@ -2005,6 +2010,10 @@
|
||||
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound, flash the taskbar icon (if the terminal window is not in focus) and flash the window. An array of specific behaviors can also be used. Supported array values include `audible`, `window` and `taskbar`. When set to \"none\", nothing will happen.",
|
||||
"$ref": "#/$defs/BellStyle"
|
||||
},
|
||||
"bellSound": {
|
||||
"description": "Sets the sound played when the application emits a BEL. When set to an array, the terminal will pick one of those sounds at random.",
|
||||
"$ref": "#/$defs/BellSound"
|
||||
},
|
||||
"closeOnExit": {
|
||||
"default": "graceful",
|
||||
"description": "Sets how the profile reacts to termination or failure to launch. Possible values:\n -\"graceful\" (close when exit is typed or the process exits normally)\n -\"always\" (always close)\n -\"never\" (never close).\ntrue and false are accepted as synonyms for \"graceful\" and \"never\" respectively.",
|
||||
|
||||
@@ -357,11 +357,6 @@ void TextAttribute::SetReverseVideo(bool isReversed) noexcept
|
||||
WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_REVERSE_VIDEO, isReversed);
|
||||
}
|
||||
|
||||
ExtendedAttributes TextAttribute::GetExtendedAttributes() const noexcept
|
||||
{
|
||||
return _extendedAttrs;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - swaps foreground and background color
|
||||
void TextAttribute::Invert() noexcept
|
||||
|
||||
@@ -109,7 +109,10 @@ public:
|
||||
void SetOverlined(bool isOverlined) noexcept;
|
||||
void SetReverseVideo(bool isReversed) noexcept;
|
||||
|
||||
ExtendedAttributes GetExtendedAttributes() const noexcept;
|
||||
constexpr ExtendedAttributes GetExtendedAttributes() const noexcept
|
||||
{
|
||||
return _extendedAttrs;
|
||||
}
|
||||
|
||||
bool IsHyperlink() const noexcept;
|
||||
|
||||
@@ -161,6 +164,13 @@ public:
|
||||
{
|
||||
return WI_IsAnyFlagSet(_wAttrLegacy, COMMON_LVB_GRID_HORIZONTAL | COMMON_LVB_GRID_LVERTICAL | COMMON_LVB_GRID_RVERTICAL | COMMON_LVB_UNDERSCORE);
|
||||
}
|
||||
constexpr bool HasAnyExtendedAttributes() const noexcept
|
||||
{
|
||||
return GetExtendedAttributes() != ExtendedAttributes::Normal ||
|
||||
IsAnyGridLineEnabled() ||
|
||||
GetHyperlinkId() != 0 ||
|
||||
IsReverseVideo();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::array<TextColor, 16> s_legacyForegroundColorMap;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(OpenConsoleDir)src\wap-common.build.pre.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<!--
|
||||
These two properties are very important!
|
||||
@@ -135,6 +140,10 @@
|
||||
<!-- 16.3.0 - remove non-resources.pri PRI files since we just forced them back in. -->
|
||||
<AppxPackagePayload Remove="@(AppxPackagePayload)" Condition="'%(Extension)' == '.pri' and '%(Filename)' != 'resources'" />
|
||||
<AppxUploadPackagePayload Remove="@(AppxUploadPackagePayload)" Condition="'%(Extension)' == '.pri' and '%(Filename)' != 'resources'" />
|
||||
|
||||
<!-- Remove all of the xaml files, because we are using embedded xbf payloads (saves about 500kb on disk!) -->
|
||||
<AppxPackagePayload Remove="@(AppxPackagePayload)" Condition="'%(Extension)' == '.xaml'" />
|
||||
<AppxUploadPackagePayload Remove="@(AppxUploadPackagePayload)" Condition="'%(Extension)' == '.xaml'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
@@ -160,13 +169,7 @@
|
||||
</Target>
|
||||
|
||||
<!-- This is required to get the package dependency in the AppXManifest. -->
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -153,10 +153,6 @@
|
||||
|
||||
<StaticResource x:Key="UnfocusedBorderBrush"
|
||||
ResourceKey="ApplicationPageBackgroundThemeBrush" />
|
||||
|
||||
<StaticResource x:Key="SettingsPageBackground"
|
||||
ResourceKey="SolidBackgroundFillColorTertiary" />
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
@@ -170,9 +166,6 @@
|
||||
|
||||
<StaticResource x:Key="UnfocusedBorderBrush"
|
||||
ResourceKey="ApplicationPageBackgroundThemeBrush" />
|
||||
|
||||
<StaticResource x:Key="SettingsPageBackground"
|
||||
ResourceKey="SolidBackgroundFillColorTertiary" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
@@ -190,9 +183,6 @@
|
||||
-->
|
||||
<StaticResource x:Key="TabViewBackground"
|
||||
ResourceKey="SystemColorButtonFaceColorBrush" />
|
||||
|
||||
<StaticResource x:Key="SettingsPageBackground"
|
||||
ResourceKey="SystemColorWindowColorBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
@@ -292,7 +292,7 @@ namespace winrt::TerminalApp::implementation
|
||||
_root->Maximized(true);
|
||||
}
|
||||
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode))
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::FullscreenMode) && !IsQuakeWindow())
|
||||
{
|
||||
_root->SetFullscreen(true);
|
||||
}
|
||||
|
||||
@@ -199,7 +199,6 @@ namespace winrt::TerminalApp::implementation
|
||||
FORWARDED_TYPED_EVENT(FullscreenChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, FullscreenChanged);
|
||||
FORWARDED_TYPED_EVENT(ChangeMaximizeRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, ChangeMaximizeRequested);
|
||||
FORWARDED_TYPED_EVENT(AlwaysOnTopChanged, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, AlwaysOnTopChanged);
|
||||
FORWARDED_TYPED_EVENT(Initialized, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, Initialized);
|
||||
FORWARDED_TYPED_EVENT(RaiseVisualBell, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, RaiseVisualBell);
|
||||
FORWARDED_TYPED_EVENT(SetTaskbarProgress, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable, _root, SetTaskbarProgress);
|
||||
FORWARDED_TYPED_EVENT(IdentifyWindowsRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IdentifyWindowsRequested);
|
||||
|
||||
@@ -122,7 +122,6 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ChangeMaximizeRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> Initialized;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> RaiseVisualBell;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
|
||||
@@ -111,7 +111,15 @@ namespace winrt::Microsoft::TerminalApp::implementation
|
||||
|
||||
void DebugTapConnection::_OutputHandler(const hstring str)
|
||||
{
|
||||
_TerminalOutputHandlers(til::visualize_control_codes(str));
|
||||
auto output = til::visualize_control_codes(str);
|
||||
// To make the output easier to read, we introduce a line break whenever
|
||||
// an LF control is encountered. But at this point, the LF would have
|
||||
// been converted to U+240A (␊), so that's what we need to search for.
|
||||
for (size_t lfPos = 0; (lfPos = output.find(L'\u240A', lfPos)) != std::wstring::npos;)
|
||||
{
|
||||
output.insert(++lfPos, L"\r\n");
|
||||
}
|
||||
_TerminalOutputHandlers(output);
|
||||
}
|
||||
|
||||
// Called by the DebugInputTapConnection to print user input
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
</resheader>
|
||||
<data name="AppName" xml:space="preserve">
|
||||
<value>Terminal</value>
|
||||
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
|
||||
</data>
|
||||
<data name="AppNameDev" xml:space="preserve">
|
||||
<value>Terminal Dev</value>
|
||||
@@ -126,9 +127,11 @@
|
||||
</data>
|
||||
<data name="AppNamePre" xml:space="preserve">
|
||||
<value>Terminal Preview</value>
|
||||
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
|
||||
</data>
|
||||
<data name="AppStoreName" xml:space="preserve">
|
||||
<value>Windows Terminal</value>
|
||||
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
|
||||
</data>
|
||||
<data name="AppStoreNameDev" xml:space="preserve">
|
||||
<value>Windows Terminal Dev</value>
|
||||
@@ -136,9 +139,11 @@
|
||||
</data>
|
||||
<data name="AppStoreNamePre" xml:space="preserve">
|
||||
<value>Windows Terminal Preview</value>
|
||||
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
|
||||
</data>
|
||||
<data name="AppShortName" xml:space="preserve">
|
||||
<value>Terminal</value>
|
||||
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
|
||||
</data>
|
||||
<data name="AppShortNameDev" xml:space="preserve">
|
||||
<value>Terminal Dev</value>
|
||||
@@ -146,6 +151,7 @@
|
||||
</data>
|
||||
<data name="AppShortNamePre" xml:space="preserve">
|
||||
<value>Terminal Preview</value>
|
||||
<comment>{Locked=qps-ploc,qps-ploca,qps-plocm}</comment>
|
||||
</data>
|
||||
<data name="AppDescription" xml:space="preserve">
|
||||
<value>The New Windows Terminal</value>
|
||||
@@ -158,15 +164,15 @@
|
||||
<value>Windows Terminal with a preview of upcoming features</value>
|
||||
</data>
|
||||
<data name="ShellExtension_OpenInTerminalMenuItem_Dev" xml:space="preserve">
|
||||
<value>Open in Terminal (Dev)</value>
|
||||
<value>Open in Terminal (&Dev)</value>
|
||||
<comment>{Locked} The dev build will never be seen in multiple languages</comment>
|
||||
</data>
|
||||
<data name="ShellExtension_OpenInTerminalMenuItem_Preview" xml:space="preserve">
|
||||
<value>Open in Terminal Preview</value>
|
||||
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal</comment>
|
||||
<value>Open in Terminal &Preview</value>
|
||||
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the Preview version of Windows Terminal. Please mark one of the characters to be an accelerator key.</comment>
|
||||
</data>
|
||||
<data name="ShellExtension_OpenInTerminalMenuItem" xml:space="preserve">
|
||||
<value>Open in Terminal</value>
|
||||
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal</comment>
|
||||
<value>Open in &Terminal</value>
|
||||
<comment>This is a menu item that will be displayed in the Windows File Explorer that launches the non-preview version of Windows Terminal. Please mark one of the characters to be an accelerator key.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -709,6 +709,9 @@
|
||||
<data name="ExportSuccess" xml:space="preserve">
|
||||
<value>Successfully exported terminal content</value>
|
||||
</data>
|
||||
<data name="FindText" xml:space="preserve">
|
||||
<value>Find</value>
|
||||
</data>
|
||||
<data name="PlainText" xml:space="preserve">
|
||||
<value>Plain Text</value>
|
||||
</data>
|
||||
|
||||
@@ -209,6 +209,16 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
newTabImpl->FindRequested([weakTab, weakThis{ get_weak() }]() {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_Find();
|
||||
}
|
||||
});
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
|
||||
|
||||
@@ -9,8 +9,11 @@
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
||||
Background="{ThemeResource TabViewBackground}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- GH#13143: Make sure that the Background is actually TabViewBackground here, not Transparent. This is load bearing, for showTabsInTitlebar=false. -->
|
||||
|
||||
<mux:TabView x:Name="TabView"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
@@ -57,7 +60,8 @@
|
||||
FontSize="12">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Placement="Mouse">
|
||||
<TextBlock IsTextSelectionEnabled="False">
|
||||
<TextBlock IsTextSelectionEnabled="False"
|
||||
TextWrapping="Wrap">
|
||||
<Run x:Uid="NewTabRun" /> <LineBreak />
|
||||
<Run x:Uid="NewPaneRun"
|
||||
FontStyle="Italic" /> <LineBreak />
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
@@ -407,13 +408,6 @@
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
<!--
|
||||
By default, the PRI file will contain resource paths beginning with the
|
||||
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
|
||||
|
||||
@@ -69,6 +69,29 @@ namespace winrt::TerminalApp::implementation
|
||||
// - see GH#2988
|
||||
HRESULT TerminalPage::Initialize(HWND hwnd)
|
||||
{
|
||||
if (!_hostingHwnd.has_value())
|
||||
{
|
||||
// GH#13211 - if we haven't yet set the owning hwnd, reparent all the controls now.
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
|
||||
if (const auto& term{ pane->GetTerminalControl() })
|
||||
{
|
||||
term.OwningHwnd(reinterpret_cast<uint64_t>(hwnd));
|
||||
}
|
||||
});
|
||||
}
|
||||
// We don't need to worry about resetting the owning hwnd for the
|
||||
// SUI here. GH#13211 only repros for a defterm connection, where
|
||||
// the tab is spawned before the window is created. It's not
|
||||
// possible to make a SUI tab like that, before the window is
|
||||
// created. The SUI could be spawned as a part of a window restore,
|
||||
// but that would still work fine. The window would be created
|
||||
// before restoring previous tabs in that scenario.
|
||||
}
|
||||
}
|
||||
_hostingHwnd = hwnd;
|
||||
return S_OK;
|
||||
}
|
||||
@@ -218,6 +241,24 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Inform the host that our titlebar content has changed.
|
||||
_SetTitleBarContentHandlers(*this, _tabRow);
|
||||
|
||||
// GH#13143 Manually set the tab row's background to transparent here.
|
||||
//
|
||||
// We're doing it this way because ThemeResources are tricky. We
|
||||
// default in XAML to using the appropriate ThemeResource background
|
||||
// color for our TabRow. When tabs in the titlebar are _disabled_,
|
||||
// this will ensure that the tab row has the correct theme-dependent
|
||||
// value. When tabs in the titlebar are _enabled_ (the default),
|
||||
// we'll switch the BG to Transparent, to let the Titlebar Control's
|
||||
// background be used as the BG for the tab row.
|
||||
//
|
||||
// We can't do it the other way around (default to Transparent, only
|
||||
// switch to a color when disabling tabs in the titlebar), because
|
||||
// looking up the correct ThemeResource from and App dictionary is a
|
||||
// capital-H Hard problem.
|
||||
const auto transparent = Media::SolidColorBrush();
|
||||
transparent.Color(Windows::UI::Colors::Transparent());
|
||||
_tabRow.Background(transparent);
|
||||
}
|
||||
|
||||
// Hookup our event handlers to the ShortcutActionDispatch
|
||||
@@ -659,7 +700,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget TerminalPage::_CompleteInitialization()
|
||||
void TerminalPage::_CompleteInitialization()
|
||||
{
|
||||
_startupState = StartupState::Initialized;
|
||||
|
||||
@@ -679,55 +720,10 @@ namespace winrt::TerminalApp::implementation
|
||||
if (_tabs.Size() == 0 && !(_shouldStartInboundListener || _isEmbeddingInboundListener))
|
||||
{
|
||||
_LastTabClosedHandlers(*this, nullptr);
|
||||
co_return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// GH#11561: When we start up, our window is initially just a frame
|
||||
// with a transparent content area. We're gonna do all this startup
|
||||
// init on the UI thread, so the UI won't actually paint till it's
|
||||
// all done. This results in a few frames where the frame is
|
||||
// visible, before the page paints for the first time, before any
|
||||
// tabs appears, etc.
|
||||
//
|
||||
// To mitigate this, we're gonna wait for the UI thread to finish
|
||||
// everything it's gotta do for the initial init, and _then_ fire
|
||||
// our Initialized event. By waiting for everything else to finish
|
||||
// (CoreDispatcherPriority::Low), we let all the tabs and panes
|
||||
// actually get created. In the window layer, we're gonna cloak the
|
||||
// window till this event is fired, so we don't actually see this
|
||||
// frame until we're actually all ready to go.
|
||||
//
|
||||
// This will result in the window seemingly not loading as fast, but
|
||||
// it will actually take exactly the same amount of time before it's
|
||||
// usable.
|
||||
//
|
||||
// We also experimented with drawing a solid BG color before the
|
||||
// initialization is finished. However, there are still a few frames
|
||||
// after the frame is displayed before the XAML content first draws,
|
||||
// so that didn't actually resolve any issues.
|
||||
|
||||
auto weak{ get_weak() };
|
||||
|
||||
// Switch to the BG thread -
|
||||
co_await winrt::resume_background();
|
||||
|
||||
if (auto self{ weak.get() })
|
||||
{
|
||||
// Then enqueue the rest of this function for after the UI thread settles.
|
||||
co_await wil::resume_foreground(self->Dispatcher(), CoreDispatcherPriority::Low);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't exist anymore? Well, we probably don't need to fire
|
||||
// off an Initialized event then...
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (auto self{ weak.get() })
|
||||
{
|
||||
self->_InitializedHandlers(*self, nullptr);
|
||||
}
|
||||
_InitializedHandlers(*this, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1849,9 +1845,17 @@ namespace winrt::TerminalApp::implementation
|
||||
// Instead, let's just promote this first split to be a tab instead.
|
||||
// Crash avoided, and we don't need to worry about inserting a new-tab
|
||||
// command in at the start.
|
||||
if (!focusedTab && _tabs.Size() == 0)
|
||||
if (!focusedTab)
|
||||
{
|
||||
_CreateNewTabFromPane(newPane);
|
||||
if (_tabs.Size() == 0)
|
||||
{
|
||||
_CreateNewTabFromPane(newPane);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The focused tab isn't a terminal tab
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2476,7 +2480,10 @@ namespace winrt::TerminalApp::implementation
|
||||
TermControl term{ settings.DefaultSettings(), settings.UnfocusedSettings(), connection };
|
||||
|
||||
// GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now.
|
||||
term.WindowVisibilityChanged(_visible);
|
||||
if (_visible)
|
||||
{
|
||||
term.WindowVisibilityChanged(_visible);
|
||||
}
|
||||
|
||||
if (_hostingHwnd.has_value())
|
||||
{
|
||||
@@ -2596,7 +2603,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <none>
|
||||
void TerminalPage::_SetBackgroundImage(const winrt::Microsoft::Terminal::Settings::Model::IAppearanceConfig& newAppearance)
|
||||
{
|
||||
if (newAppearance.BackgroundImagePath().empty())
|
||||
const auto path = newAppearance.ExpandedBackgroundImagePath();
|
||||
if (path.empty())
|
||||
{
|
||||
_tabContent.Background(nullptr);
|
||||
return;
|
||||
@@ -2605,7 +2613,7 @@ namespace winrt::TerminalApp::implementation
|
||||
Windows::Foundation::Uri imageUri{ nullptr };
|
||||
try
|
||||
{
|
||||
imageUri = Windows::Foundation::Uri{ newAppearance.BackgroundImagePath() };
|
||||
imageUri = Windows::Foundation::Uri{ path };
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -2622,7 +2630,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
if (imageSource == nullptr ||
|
||||
imageSource.UriSource() == nullptr ||
|
||||
imageSource.UriSource().RawUri() != imageUri.RawUri())
|
||||
!imageSource.UriSource().Equals(imageUri))
|
||||
{
|
||||
Media::ImageBrush b{};
|
||||
// Note that BitmapImage handles the image load asynchronously,
|
||||
@@ -3266,6 +3274,9 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
// Request a summon of this window to the foreground
|
||||
_SummonWindowRequestedHandlers(*this, nullptr);
|
||||
|
||||
const IInspectable unused{ nullptr };
|
||||
_SetAsDefaultDismissHandler(unused, unused);
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace winrt::TerminalApp::implementation
|
||||
TYPED_EVENT(AlwaysOnTopChanged, IInspectable, IInspectable);
|
||||
TYPED_EVENT(RaiseVisualBell, IInspectable, IInspectable);
|
||||
TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
|
||||
TYPED_EVENT(Initialized, IInspectable, IInspectable);
|
||||
TYPED_EVENT(Initialized, IInspectable, winrt::Windows::UI::Xaml::RoutedEventArgs);
|
||||
TYPED_EVENT(IdentifyWindowsRequested, IInspectable, IInspectable);
|
||||
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
|
||||
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
|
||||
@@ -390,7 +390,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _StartInboundListener();
|
||||
|
||||
winrt::fire_and_forget _CompleteInitialization();
|
||||
void _CompleteInitialization();
|
||||
|
||||
void _FocusActiveControl(IInspectable sender, IInspectable eventArgs);
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace TerminalApp
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusModeChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FullscreenChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> AlwaysOnTopChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> Initialized;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Windows.UI.Xaml.RoutedEventArgs> Initialized;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SetTaskbarProgress;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
|
||||
|
||||
@@ -1273,6 +1273,23 @@ namespace winrt::TerminalApp::implementation
|
||||
exportTabMenuItem.Icon(exportTabSymbol);
|
||||
}
|
||||
|
||||
Controls::MenuFlyoutItem findMenuItem;
|
||||
{
|
||||
// "Split Tab"
|
||||
Controls::FontIcon findSymbol;
|
||||
findSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
|
||||
findSymbol.Glyph(L"\xF78B"); // SearchMedium
|
||||
|
||||
findMenuItem.Click([weakThis](auto&&, auto&&) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_FindRequestedHandlers();
|
||||
}
|
||||
});
|
||||
findMenuItem.Text(RS_(L"FindText"));
|
||||
findMenuItem.Icon(findSymbol);
|
||||
}
|
||||
|
||||
// Build the menu
|
||||
Controls::MenuFlyout contextMenuFlyout;
|
||||
Controls::MenuFlyoutSeparator menuSeparator;
|
||||
@@ -1281,6 +1298,7 @@ namespace winrt::TerminalApp::implementation
|
||||
contextMenuFlyout.Items().Append(duplicateTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(splitTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(exportTabMenuItem);
|
||||
contextMenuFlyout.Items().Append(findMenuItem);
|
||||
contextMenuFlyout.Items().Append(menuSeparator);
|
||||
|
||||
// GH#5750 - When the context menu is dismissed with ESC, toss the focus
|
||||
@@ -1291,7 +1309,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// GH#10112 - if we're opening the tab renamer, don't
|
||||
// immediately toss focus to the control. We don't want to steal
|
||||
// focus from the tab renamer.
|
||||
if (!tab->_headerControl.InRename())
|
||||
if (!tab->_headerControl.InRename() && !tab->GetActiveTerminalControl().SearchBoxEditInFocus())
|
||||
{
|
||||
tab->_RequestFocusActiveControlHandlers();
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace winrt::TerminalApp::implementation
|
||||
WINRT_CALLBACK(TabRaiseVisualBell, winrt::delegate<>);
|
||||
WINRT_CALLBACK(DuplicateRequested, winrt::delegate<>);
|
||||
WINRT_CALLBACK(SplitTabRequested, winrt::delegate<>);
|
||||
WINRT_CALLBACK(FindRequested, winrt::delegate<>);
|
||||
WINRT_CALLBACK(ExportTabRequested, winrt::delegate<>);
|
||||
TYPED_EVENT(TaskbarProgressChanged, IInspectable, IInspectable);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
@@ -92,14 +93,6 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
|
||||
@@ -53,7 +53,12 @@ CATCH_RETURN()
|
||||
HRESULT CTerminalHandoff::s_StopListening()
|
||||
{
|
||||
std::unique_lock lock{ _mtx };
|
||||
return s_StopListeningLocked();
|
||||
}
|
||||
|
||||
// See s_StopListening()
|
||||
HRESULT CTerminalHandoff::s_StopListeningLocked()
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff);
|
||||
|
||||
_pfnHandoff = nullptr;
|
||||
@@ -101,14 +106,16 @@ HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE sign
|
||||
{
|
||||
try
|
||||
{
|
||||
// Stash a local copy of _pfnHandoff before we stop listening.
|
||||
std::unique_lock lock{ _mtx };
|
||||
|
||||
// s_StopListeningLocked sets _pfnHandoff to nullptr.
|
||||
// localPfnHandoff is tested for nullness below.
|
||||
#pragma warning(suppress : 26429) // Symbol '...' is never tested for nullness, it can be marked as not_null (f.23).
|
||||
auto localPfnHandoff = _pfnHandoff;
|
||||
|
||||
// Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call.
|
||||
// COM does not automatically clean that up for us. We must do it.
|
||||
s_StopListening();
|
||||
|
||||
std::unique_lock lock{ _mtx };
|
||||
LOG_IF_FAILED(s_StopListeningLocked());
|
||||
|
||||
// Report an error if no one registered a handoff function before calling this.
|
||||
THROW_HR_IF_NULL(E_NOT_VALID_STATE, localPfnHandoff);
|
||||
|
||||
@@ -43,6 +43,9 @@ struct __declspec(uuid(__CLSID_CTerminalHandoff))
|
||||
|
||||
static HRESULT s_StartListening(NewHandoffFunction pfnHandoff);
|
||||
static HRESULT s_StopListening();
|
||||
|
||||
private:
|
||||
static HRESULT s_StopListeningLocked();
|
||||
};
|
||||
|
||||
// Disable warnings from the CoCreatableClass macro as the value it provides for
|
||||
|
||||
@@ -311,13 +311,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
|
||||
THROW_IF_FAILED(_CreatePseudoConsoleAndPipes(dimensions, flags, &_inPipe, &_outPipe, &_hPC));
|
||||
|
||||
// GH#12515: The conpty assumes it's hidden at the start. If we're visible, let it know now.
|
||||
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
|
||||
if (_initialParentHwnd != 0)
|
||||
{
|
||||
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
|
||||
}
|
||||
|
||||
// GH#12515: The conpty assumes it's hidden at the start. If we're visible, let it know now.
|
||||
if (_initialVisibility)
|
||||
{
|
||||
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
|
||||
}
|
||||
|
||||
THROW_IF_FAILED(_LaunchAttachedClient());
|
||||
}
|
||||
// But if it was an inbound handoff... attempt to synchronize the size of it with what our connection
|
||||
@@ -336,6 +340,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
|
||||
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), dimensions));
|
||||
THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(_initialParentHwnd)));
|
||||
|
||||
if (_initialVisibility)
|
||||
{
|
||||
THROW_IF_FAILED(ConptyShowHidePseudoConsole(_hPC.get(), _initialVisibility));
|
||||
}
|
||||
}
|
||||
|
||||
_startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||
hstring _commandline{};
|
||||
hstring _startingDirectory{};
|
||||
hstring _startingTitle{};
|
||||
bool _initialVisibility{ false };
|
||||
bool _initialVisibility{ true };
|
||||
Windows::Foundation::Collections::ValueSet _environment{ nullptr };
|
||||
guid _guid{}; // A unique session identifier for connected client
|
||||
hstring _clientName{}; // The name of the process hosted by this ConPTY connection (as of launch).
|
||||
|
||||
@@ -172,11 +172,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
});
|
||||
|
||||
_updatePatternLocations = std::make_shared<ThrottledFuncTrailing<>>(
|
||||
_dispatcher,
|
||||
// NOTE: Calling UpdatePatternLocations from a background
|
||||
// thread is a workaround for us to hit GH#12607 less often.
|
||||
_updatePatternLocations = std::make_unique<til::throttled_func_trailing<>>(
|
||||
UpdatePatternLocationsInterval,
|
||||
[weakThis = get_weak()]() {
|
||||
if (auto core{ weakThis.get() }; !core->_IsClosing())
|
||||
if (auto core{ weakThis.get() })
|
||||
{
|
||||
core->UpdatePatternLocations();
|
||||
}
|
||||
@@ -264,11 +265,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
const auto height = vp.Height();
|
||||
_connection.Resize(height, width);
|
||||
|
||||
if (_OwningHwnd != 0)
|
||||
if (_owningHwnd != 0)
|
||||
{
|
||||
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
|
||||
{
|
||||
conpty.ReparentWindow(_OwningHwnd);
|
||||
conpty.ReparentWindow(_owningHwnd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,7 +446,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// itself - it was initiated by the mouse wheel, or the scrollbar.
|
||||
_terminal->UserScrollViewport(viewTop);
|
||||
|
||||
_updatePatternLocations->Run();
|
||||
(*_updatePatternLocations)();
|
||||
}
|
||||
|
||||
void ControlCore::AdjustOpacity(const double adjustment)
|
||||
@@ -933,13 +934,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Method Description:
|
||||
// - Given a copy-able selection, get the selected text from the buffer and send it to the
|
||||
// Windows Clipboard (CascadiaWin32:main.cpp).
|
||||
// - CopyOnSelect does NOT clear the selection
|
||||
// Arguments:
|
||||
// - singleLine: collapse all of the text to one line
|
||||
// - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
|
||||
// if we should defer which formats are copied to the global setting
|
||||
// - clearSelection: if true, clear the selection. Used for CopyOnSelect.
|
||||
bool ControlCore::CopySelectionToClipboard(bool singleLine,
|
||||
const Windows::Foundation::IReference<CopyFormat>& formats)
|
||||
const Windows::Foundation::IReference<CopyFormat>& formats,
|
||||
bool clearSelection)
|
||||
{
|
||||
// no selection --> nothing to copy
|
||||
if (!_terminal->IsSelectionActive())
|
||||
@@ -979,7 +981,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
bgColor) :
|
||||
"";
|
||||
|
||||
if (!_settings->CopyOnSelect())
|
||||
if (clearSelection)
|
||||
{
|
||||
_terminal->ClearSelection();
|
||||
_renderer->TriggerSelection();
|
||||
@@ -1001,6 +1003,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_renderer->TriggerSelection();
|
||||
}
|
||||
|
||||
const bool ControlCore::IsInQuickEditMode() const noexcept
|
||||
{
|
||||
return _terminal->IsInQuickEditMode();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Pre-process text pasted (presumably from the clipboard)
|
||||
// before sending it over the terminal's connection.
|
||||
@@ -1193,7 +1200,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
|
||||
// Additionally, start the throttled update of where our links are.
|
||||
_updatePatternLocations->Run();
|
||||
(*_updatePatternLocations)();
|
||||
}
|
||||
|
||||
void ControlCore::_terminalCursorPositionChanged()
|
||||
@@ -1210,8 +1217,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void ControlCore::_terminalShowWindowChanged(bool showOrHide)
|
||||
{
|
||||
auto showWindow = winrt::make_self<implementation::ShowWindowArgs>(showOrHide);
|
||||
_ShowWindowChangedHandlers(*this, *showWindow);
|
||||
if (_initializedTerminal)
|
||||
{
|
||||
auto showWindow = winrt::make_self<implementation::ShowWindowArgs>(showOrHide);
|
||||
_ShowWindowChangedHandlers(*this, *showWindow);
|
||||
}
|
||||
}
|
||||
|
||||
bool ControlCore::HasSelection() const
|
||||
@@ -1502,7 +1512,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_terminal->Write(hstr);
|
||||
|
||||
// Start the throttled update of where our hyperlinks are.
|
||||
_updatePatternLocations->Run();
|
||||
(*_updatePatternLocations)();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1712,10 +1722,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - <none>
|
||||
void ControlCore::WindowVisibilityChanged(const bool showOrHide)
|
||||
{
|
||||
// show is true, hide is false
|
||||
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
|
||||
if (_initializedTerminal)
|
||||
{
|
||||
conpty.ShowHide(showOrHide);
|
||||
// show is true, hide is false
|
||||
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
|
||||
{
|
||||
conpty.ShowHide(showOrHide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1731,13 +1744,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - This is related to work done for GH#2988.
|
||||
void ControlCore::GotFocus()
|
||||
{
|
||||
_terminal->FocusChanged(true);
|
||||
_focusChanged(true);
|
||||
}
|
||||
|
||||
// See GotFocus.
|
||||
void ControlCore::LostFocus()
|
||||
{
|
||||
_terminal->FocusChanged(false);
|
||||
_focusChanged(false);
|
||||
}
|
||||
|
||||
void ControlCore::_focusChanged(bool focused)
|
||||
{
|
||||
// GH#13461 - temporarily turn off read-only mode, send the focus event,
|
||||
// then turn it back on. Even in focus mode, focus events are fine to
|
||||
// send. We don't want to pop a warning every time the control is
|
||||
// focused.
|
||||
const auto previous = std::exchange(_isReadOnly, false);
|
||||
const auto restore = wil::scope_exit([&]() { _isReadOnly = previous; });
|
||||
_terminal->FocusChanged(focused);
|
||||
}
|
||||
|
||||
bool ControlCore::_isBackgroundTransparent()
|
||||
@@ -1752,4 +1776,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// transparency, or our acrylic, or our image.
|
||||
return Opacity() < 1.0f || UseAcrylic() || !_settings->BackgroundImage().empty() || _settings->UseBackgroundImageForWindow();
|
||||
}
|
||||
|
||||
uint64_t ControlCore::OwningHwnd()
|
||||
{
|
||||
return _owningHwnd;
|
||||
}
|
||||
|
||||
void ControlCore::OwningHwnd(uint64_t owner)
|
||||
{
|
||||
if (owner != _owningHwnd && _connection)
|
||||
{
|
||||
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
|
||||
{
|
||||
conpty.ReparentWindow(owner);
|
||||
}
|
||||
}
|
||||
_owningHwnd = owner;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void SendInput(const winrt::hstring& wstr);
|
||||
void PasteText(const winrt::hstring& hstr);
|
||||
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
|
||||
bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats, bool clearSelection = true);
|
||||
void SelectAll();
|
||||
const bool IsInQuickEditMode() const noexcept;
|
||||
|
||||
void GotFocus();
|
||||
void LostFocus();
|
||||
@@ -175,10 +176,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void WindowVisibilityChanged(const bool showOrHide);
|
||||
|
||||
// TODO:GH#1256 - When a tab can be torn out or otherwise reparented to
|
||||
// another window, this value will need a custom setter, so that we can
|
||||
// also update the connection.
|
||||
WINRT_PROPERTY(uint64_t, OwningHwnd, 0);
|
||||
uint64_t OwningHwnd();
|
||||
void OwningHwnd(uint64_t owner);
|
||||
|
||||
RUNTIME_SETTING(double, Opacity, _settings->Opacity());
|
||||
RUNTIME_SETTING(bool, UseAcrylic, _settings->UseAcrylic());
|
||||
@@ -248,9 +247,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
double _panelHeight{ 0 };
|
||||
double _compositionScale{ 0 };
|
||||
|
||||
uint64_t _owningHwnd{ 0 };
|
||||
|
||||
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _tsfTryRedrawCanvas;
|
||||
std::shared_ptr<ThrottledFuncTrailing<>> _updatePatternLocations;
|
||||
std::unique_ptr<til::throttled_func_trailing<>> _updatePatternLocations;
|
||||
std::shared_ptr<ThrottledFuncTrailing<Control::ScrollPositionChangedArgs>> _updateScrollBar;
|
||||
|
||||
winrt::fire_and_forget _asyncCloseConnection();
|
||||
@@ -287,6 +288,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _setOpacity(const double opacity);
|
||||
|
||||
bool _isBackgroundTransparent();
|
||||
void _focusChanged(bool focused);
|
||||
|
||||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
|
||||
@@ -142,13 +142,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Method Description:
|
||||
// - Given a copy-able selection, get the selected text from the buffer and send it to the
|
||||
// Windows Clipboard (CascadiaWin32:main.cpp).
|
||||
// - CopyOnSelect does NOT clear the selection
|
||||
// Arguments:
|
||||
// - singleLine: collapse all of the text to one line
|
||||
// - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
|
||||
// if we should defer which formats are copied to the global setting
|
||||
// - clearSelection: if true, clear the selection after copying it. Used for CopyOnSelect.
|
||||
bool ControlInteractivity::CopySelectionToClipboard(bool singleLine,
|
||||
const Windows::Foundation::IReference<CopyFormat>& formats)
|
||||
const Windows::Foundation::IReference<CopyFormat>& formats,
|
||||
bool clearSelection)
|
||||
{
|
||||
if (_core)
|
||||
{
|
||||
@@ -164,7 +165,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// Mark the current selection as copied
|
||||
_selectionNeedsToBeCopied = false;
|
||||
|
||||
return _core->CopySelectionToClipboard(singleLine, formats);
|
||||
return _core->CopySelectionToClipboard(singleLine, formats, clearSelection);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -257,14 +258,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
|
||||
{
|
||||
// CopyOnSelect right click always pastes
|
||||
if (_core->CopyOnSelect() || !_core->HasSelection())
|
||||
if (_core->CopyOnSelect())
|
||||
{
|
||||
// CopyOnSelect:
|
||||
// 1. keyboard selection? --> copy the new content first
|
||||
// 2. right click always pastes!
|
||||
if (_core->IsInQuickEditMode())
|
||||
{
|
||||
CopySelectionToClipboard(shiftEnabled, nullptr);
|
||||
}
|
||||
RequestPasteTextFromClipboard();
|
||||
}
|
||||
else if (_core->HasSelection())
|
||||
{
|
||||
// copy selected text
|
||||
CopySelectionToClipboard(shiftEnabled, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
CopySelectionToClipboard(shiftEnabled, nullptr);
|
||||
// no selection --> paste
|
||||
RequestPasteTextFromClipboard();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,7 +396,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
isLeftMouseRelease &&
|
||||
_selectionNeedsToBeCopied)
|
||||
{
|
||||
CopySelectionToClipboard(false, nullptr);
|
||||
// IMPORTANT!
|
||||
// Set clearSelection to false here!
|
||||
// Otherwise, the selection will be cleared immediately after you make it.
|
||||
CopySelectionToClipboard(false, nullptr, /*clearSelection*/ false);
|
||||
}
|
||||
|
||||
_singleClickTouchdownPos = std::nullopt;
|
||||
|
||||
@@ -80,7 +80,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
#pragma endregion
|
||||
|
||||
bool CopySelectionToClipboard(bool singleLine,
|
||||
const Windows::Foundation::IReference<CopyFormat>& formats);
|
||||
const Windows::Foundation::IReference<CopyFormat>& formats,
|
||||
bool clearSelection = true);
|
||||
void RequestPasteTextFromClipboard();
|
||||
void SetEndSelectionPoint(const Core::Point pixelPosition);
|
||||
bool ManglePathsForWsl();
|
||||
|
||||
@@ -197,6 +197,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// Find if search box text edit currently is in focus
|
||||
// Return Value:
|
||||
// - true, if search box text edit is in focus
|
||||
bool TermControl::SearchBoxEditInFocus() const
|
||||
{
|
||||
if (!_searchBox)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _searchBox->TextBox().FocusState() == FocusState::Keyboard;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Search text in text buffer. This is triggered if the user click
|
||||
// search button or press enter.
|
||||
@@ -426,7 +440,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
if (imageSource == nullptr ||
|
||||
imageSource.UriSource() == nullptr ||
|
||||
imageSource.UriSource().RawUri() != imageUri.RawUri())
|
||||
!imageSource.UriSource().Equals(imageUri))
|
||||
{
|
||||
// Note that BitmapImage handles the image load asynchronously,
|
||||
// which is especially important since the image
|
||||
|
||||
@@ -90,6 +90,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
void SearchMatch(const bool goForward);
|
||||
|
||||
bool SearchBoxEditInFocus() const;
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
|
||||
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Microsoft.Terminal.Control
|
||||
void ScrollViewport(Int32 viewTop);
|
||||
|
||||
void CreateSearchBoxControl();
|
||||
Boolean SearchBoxEditInFocus();
|
||||
|
||||
void SearchMatch(Boolean goForward);
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ Terminal::Terminal() :
|
||||
_snapOnInput{ true },
|
||||
_altGrAliasing{ true },
|
||||
_blockSelection{ false },
|
||||
_quickEditMode{ false },
|
||||
_selection{ std::nullopt },
|
||||
_taskbarState{ 0 },
|
||||
_taskbarProgress{ 0 },
|
||||
@@ -989,7 +990,7 @@ int Terminal::ViewStartIndex() const noexcept
|
||||
|
||||
int Terminal::ViewEndIndex() const noexcept
|
||||
{
|
||||
return _inAltBuffer() ? _altBufferSize.height : _mutableViewport.BottomInclusive();
|
||||
return _inAltBuffer() ? _altBufferSize.height - 1 : _mutableViewport.BottomInclusive();
|
||||
}
|
||||
|
||||
// _VisibleStartIndex is the first visible line of the buffer
|
||||
|
||||
@@ -241,6 +241,7 @@ public:
|
||||
void SetBlockSelection(const bool isEnabled) noexcept;
|
||||
void UpdateSelection(SelectionDirection direction, SelectionExpansion mode);
|
||||
void SelectAll();
|
||||
bool IsInQuickEditMode() const noexcept;
|
||||
|
||||
using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
|
||||
static UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey);
|
||||
@@ -313,6 +314,7 @@ private:
|
||||
bool _blockSelection;
|
||||
std::wstring _wordDelimiters;
|
||||
SelectionExpansion _multiClickSelectionMode;
|
||||
bool _quickEditMode;
|
||||
#pragma endregion
|
||||
|
||||
std::unique_ptr<TextBuffer> _mainBuffer;
|
||||
|
||||
@@ -96,6 +96,11 @@ const bool Terminal::IsBlockSelection() const noexcept
|
||||
return _blockSelection;
|
||||
}
|
||||
|
||||
bool Terminal::IsInQuickEditMode() const noexcept
|
||||
{
|
||||
return _quickEditMode;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Perform a multi-click selection at viewportPos expanding according to the expansionMode
|
||||
// Arguments:
|
||||
@@ -288,7 +293,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
|
||||
const auto movingEnd{ _selection->start == _selection->pivot };
|
||||
auto targetPos{ movingEnd ? _selection->end : _selection->start };
|
||||
|
||||
// 2. Perform the movement
|
||||
// 2 Perform the movement
|
||||
switch (mode)
|
||||
{
|
||||
case SelectionExpansion::Char:
|
||||
@@ -308,12 +313,13 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
|
||||
// 3. Actually modify the selection
|
||||
// NOTE: targetStart doesn't matter here
|
||||
auto targetStart = false;
|
||||
_quickEditMode = true;
|
||||
std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
|
||||
|
||||
// 4. Scroll (if necessary)
|
||||
if (const auto viewport = _GetVisibleViewport(); !viewport.IsInBounds(targetPos))
|
||||
if (const auto visibleViewport = _GetVisibleViewport(); !visibleViewport.IsInBounds(targetPos))
|
||||
{
|
||||
if (const auto amtAboveView = viewport.Top() - targetPos.Y; amtAboveView > 0)
|
||||
if (const auto amtAboveView = visibleViewport.Top() - targetPos.Y; amtAboveView > 0)
|
||||
{
|
||||
// anchor is above visible viewport, scroll by that amount
|
||||
_scrollOffset += amtAboveView;
|
||||
@@ -321,7 +327,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
|
||||
else
|
||||
{
|
||||
// anchor is below visible viewport, scroll by that amount
|
||||
const auto amtBelowView = targetPos.Y - viewport.BottomInclusive();
|
||||
const auto amtBelowView = targetPos.Y - visibleViewport.BottomInclusive();
|
||||
_scrollOffset -= amtBelowView;
|
||||
}
|
||||
_NotifyScrollEvent();
|
||||
@@ -353,13 +359,16 @@ void Terminal::_MoveByChar(SelectionDirection direction, COORD& pos)
|
||||
case SelectionDirection::Up:
|
||||
{
|
||||
const auto bufferSize{ _activeBuffer().GetSize() };
|
||||
pos = { pos.X, std::clamp(base::ClampSub<short, short>(pos.Y, 1).RawValue(), bufferSize.Top(), bufferSize.BottomInclusive()) };
|
||||
const auto newY{ base::ClampSub<short, short>(pos.Y, 1).RawValue() };
|
||||
pos = newY < bufferSize.Top() ? bufferSize.Origin() : COORD{ pos.X, newY };
|
||||
break;
|
||||
}
|
||||
case SelectionDirection::Down:
|
||||
{
|
||||
const auto bufferSize{ _activeBuffer().GetSize() };
|
||||
pos = { pos.X, std::clamp(base::ClampAdd<short, short>(pos.Y, 1).RawValue(), bufferSize.Top(), bufferSize.BottomInclusive()) };
|
||||
const auto mutableBottom{ _GetMutableViewport().BottomInclusive() };
|
||||
const auto newY{ base::ClampAdd<short, short>(pos.Y, 1).RawValue() };
|
||||
pos = newY > mutableBottom ? COORD{ bufferSize.RightInclusive(), mutableBottom } : COORD{ pos.X, newY };
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -473,6 +482,7 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, COORD& pos)
|
||||
void Terminal::ClearSelection()
|
||||
{
|
||||
_selection = std::nullopt;
|
||||
_quickEditMode = false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -36,22 +36,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
static constexpr std::wstring_view SelectionBackgroundColorTag{ L"SelectionBackground" };
|
||||
|
||||
static const std::array<hstring, 16> TableColorNames = {
|
||||
RS_(L"ColorScheme_Black/Header"),
|
||||
RS_(L"ColorScheme_Red/Header"),
|
||||
RS_(L"ColorScheme_Green/Header"),
|
||||
RS_(L"ColorScheme_Yellow/Header"),
|
||||
RS_(L"ColorScheme_Blue/Header"),
|
||||
RS_(L"ColorScheme_Purple/Header"),
|
||||
RS_(L"ColorScheme_Cyan/Header"),
|
||||
RS_(L"ColorScheme_White/Header"),
|
||||
RS_(L"ColorScheme_BrightBlack/Header"),
|
||||
RS_(L"ColorScheme_BrightRed/Header"),
|
||||
RS_(L"ColorScheme_BrightGreen/Header"),
|
||||
RS_(L"ColorScheme_BrightYellow/Header"),
|
||||
RS_(L"ColorScheme_BrightBlue/Header"),
|
||||
RS_(L"ColorScheme_BrightPurple/Header"),
|
||||
RS_(L"ColorScheme_BrightCyan/Header"),
|
||||
RS_(L"ColorScheme_BrightWhite/Header")
|
||||
RS_(L"ColorScheme_Black/Text"),
|
||||
RS_(L"ColorScheme_Red/Text"),
|
||||
RS_(L"ColorScheme_Green/Text"),
|
||||
RS_(L"ColorScheme_Yellow/Text"),
|
||||
RS_(L"ColorScheme_Blue/Text"),
|
||||
RS_(L"ColorScheme_Purple/Text"),
|
||||
RS_(L"ColorScheme_Cyan/Text"),
|
||||
RS_(L"ColorScheme_White/Text"),
|
||||
RS_(L"ColorScheme_BrightBlack/Text"),
|
||||
RS_(L"ColorScheme_BrightRed/Text"),
|
||||
RS_(L"ColorScheme_BrightGreen/Text"),
|
||||
RS_(L"ColorScheme_BrightYellow/Text"),
|
||||
RS_(L"ColorScheme_BrightBlue/Text"),
|
||||
RS_(L"ColorScheme_BrightPurple/Text"),
|
||||
RS_(L"ColorScheme_BrightCyan/Text"),
|
||||
RS_(L"ColorScheme_BrightWhite/Text")
|
||||
};
|
||||
|
||||
static const std::array<std::wstring, 9> InBoxSchemes = {
|
||||
@@ -187,7 +187,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
if (std::find(std::begin(InBoxSchemes), std::end(InBoxSchemes), schemeName) != std::end(InBoxSchemes))
|
||||
{
|
||||
// load disclaimer for in-box profiles
|
||||
disclaimer = RS_(L"ColorScheme_DeleteButtonDisclaimerInBox");
|
||||
disclaimer = RS_(L"ColorScheme_DeleteButtonDisclaimerInBox/Text");
|
||||
}
|
||||
DeleteButtonDisclaimer().Text(disclaimer);
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
L"qps-PLOCA",
|
||||
L"qps-PLOCM",
|
||||
L"ru",
|
||||
L"zh-Hans-CN",
|
||||
L"zh-Hant-TW",
|
||||
L"zh-Hans",
|
||||
L"zh-Hant",
|
||||
};
|
||||
|
||||
GlobalAppearance::GlobalAppearance()
|
||||
|
||||
@@ -594,7 +594,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
// navigate to the profile next to this one
|
||||
const auto newSelectedItem{ menuItems.GetAt(index < menuItems.Size() - 1 ? index : index - 1) };
|
||||
SettingsNav().SelectedItem(newSelectedItem);
|
||||
_Navigate(newSelectedItem.try_as<MUX::Controls::NavigationViewItem>().Tag().try_as<Editor::ProfileViewModel>(), BreadcrumbSubPage::None, true);
|
||||
const auto newTag = newSelectedItem.as<MUX::Controls::NavigationViewItem>().Tag();
|
||||
if (const auto profileViewModel = newTag.try_as<ProfileViewModel>())
|
||||
{
|
||||
_Navigate(*profileViewModel, BreadcrumbSubPage::None);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Navigate(newTag.as<hstring>(), BreadcrumbSubPage::None);
|
||||
}
|
||||
}
|
||||
|
||||
IObservableVector<IInspectable> MainPage::Breadcrumbs() noexcept
|
||||
|
||||
@@ -9,13 +9,43 @@
|
||||
xmlns:local="using:Microsoft.Terminal.Settings.Editor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
Background="{ThemeResource SettingsPageBackground}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="CommonResources.xaml" />
|
||||
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!--
|
||||
These two values are "SolidBackgroundFillColorTertiary".
|
||||
We can't just put those here though as
|
||||
StaticResources, because XAML will evaluate their
|
||||
values when the App is first loaded, and we'll
|
||||
always use the value from the OS theme, regardless
|
||||
of the requested theme. Kinda the same thing we've
|
||||
had to do with TabViewBackground in the past.
|
||||
-->
|
||||
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<Color x:Key="SettingsPageBackground">#282828</Color>
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<Color x:Key="SettingsPageBackground">#F9F9F9</Color>
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<!-- Define resources for HighContrast mode here -->
|
||||
<StaticResource x:Key="SettingsPageBackground"
|
||||
ResourceKey="SystemColorWindowColorBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<SolidColorBrush x:Key="NavigationViewExpandedPaneBackground"
|
||||
@@ -30,7 +60,7 @@
|
||||
</Page.Resources>
|
||||
|
||||
<muxc:NavigationView x:Name="SettingsNav"
|
||||
Background="Transparent"
|
||||
Background="{ThemeResource SettingsPageBackground}"
|
||||
IsBackButtonVisible="Collapsed"
|
||||
IsSettingsVisible="False"
|
||||
ItemInvoked="SettingsNav_ItemInvoked"
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
@@ -353,12 +354,5 @@
|
||||
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
Model::TerminalSettings ProfileViewModel::TermSettings() const
|
||||
{
|
||||
return Model::TerminalSettings::CreateWithProfile(_appSettings, _profile, nullptr).DefaultSettings();
|
||||
return Model::TerminalSettings::CreateForPreview(_appSettings, _profile);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
||||
@@ -139,43 +139,43 @@
|
||||
<value>Background</value>
|
||||
<comment>This is the header for a control that lets the user select the background color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Black.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_Black.Text" xml:space="preserve">
|
||||
<value>Black</value>
|
||||
<comment>This is the header for a control that lets the user select the black color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Blue.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_Blue.Text" xml:space="preserve">
|
||||
<value>Blue</value>
|
||||
<comment>This is the header for a control that lets the user select the blue color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightBlack.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightBlack.Text" xml:space="preserve">
|
||||
<value>Bright black</value>
|
||||
<comment>This is the header for a control that lets the user select the bright black color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightBlue.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightBlue.Text" xml:space="preserve">
|
||||
<value>Bright blue</value>
|
||||
<comment>This is the header for a control that lets the user select the bright blue color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightCyan.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightCyan.Text" xml:space="preserve">
|
||||
<value>Bright cyan</value>
|
||||
<comment>This is the header for a control that lets the user select the bright cyan color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightGreen.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightGreen.Text" xml:space="preserve">
|
||||
<value>Bright green</value>
|
||||
<comment>This is the header for a control that lets the user select the bright green color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightPurple.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightPurple.Text" xml:space="preserve">
|
||||
<value>Bright purple</value>
|
||||
<comment>This is the header for a control that lets the user select the bright purple color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightRed.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightRed.Text" xml:space="preserve">
|
||||
<value>Bright red</value>
|
||||
<comment>This is the header for a control that lets the user select the bright red color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightWhite.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightWhite.Text" xml:space="preserve">
|
||||
<value>Bright white</value>
|
||||
<comment>This is the header for a control that lets the user select the bright white color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_BrightYellow.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_BrightYellow.Text" xml:space="preserve">
|
||||
<value>Bright yellow</value>
|
||||
<comment>This is the header for a control that lets the user select the bright yellow color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
@@ -183,7 +183,7 @@
|
||||
<value>Cursor color</value>
|
||||
<comment>This is the header for a control that lets the user select the text cursor's color displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Cyan.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_Cyan.Text" xml:space="preserve">
|
||||
<value>Cyan</value>
|
||||
<comment>This is the header for a control that lets the user select the cyan color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
@@ -191,11 +191,11 @@
|
||||
<value>Foreground</value>
|
||||
<comment>This is the header for a control that lets the user select the foreground color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Green.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_Green.Text" xml:space="preserve">
|
||||
<value>Green</value>
|
||||
<comment>This is the header for a control that lets the user select the green color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Purple.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_Purple.Text" xml:space="preserve">
|
||||
<value>Purple</value>
|
||||
<comment>This is the header for a control that lets the user select the purple color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
@@ -203,15 +203,15 @@
|
||||
<value>Selection background</value>
|
||||
<comment>This is the header for a control that lets the user select the background color for selected text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Red.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_Red.Text" xml:space="preserve">
|
||||
<value>Red</value>
|
||||
<comment>This is the header for a control that lets the user select the red color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_White.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_White.Text" xml:space="preserve">
|
||||
<value>White</value>
|
||||
<comment>This is the header for a control that lets the user select the white color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_Yellow.Header" xml:space="preserve">
|
||||
<data name="ColorScheme_Yellow.Text" xml:space="preserve">
|
||||
<value>Yellow</value>
|
||||
<comment>This is the header for a control that lets the user select the yellow color for text displayed on the screen.</comment>
|
||||
</data>
|
||||
@@ -1194,7 +1194,7 @@
|
||||
<value>Rename</value>
|
||||
<comment>Text label for a button that can be used to begin the renaming process.</comment>
|
||||
</data>
|
||||
<data name="ColorScheme_DeleteButtonDisclaimerInBox" xml:space="preserve">
|
||||
<data name="ColorScheme_DeleteButtonDisclaimerInBox.Text" xml:space="preserve">
|
||||
<value>This color scheme cannot be deleted or renamed because it is included by default.</value>
|
||||
<comment>Disclaimer presented next to the delete button when it is disabled.</comment>
|
||||
</data>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <shlwapi.h>
|
||||
#include <til/latch.h>
|
||||
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Settings;
|
||||
@@ -1120,12 +1121,27 @@ void CascadiaSettings::CurrentDefaultTerminal(const Model::DefaultTerminal& term
|
||||
// but in the future it might be worthwhile to change the code to use list indices instead.
|
||||
void CascadiaSettings::_refreshDefaultTerminals()
|
||||
{
|
||||
if (!_defaultTerminals)
|
||||
if (_defaultTerminals)
|
||||
{
|
||||
auto [defaultTerminals, defaultTerminal] = DefaultTerminal::Available();
|
||||
_defaultTerminals = winrt::single_threaded_observable_vector(std::move(defaultTerminals));
|
||||
_currentDefaultTerminal = std::move(defaultTerminal);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is an extract of extractValueFromTaskWithoutMainThreadAwait
|
||||
// as DefaultTerminal::Available creates the exact same issue.
|
||||
std::pair<std::vector<Model::DefaultTerminal>, Model::DefaultTerminal> result{ {}, nullptr };
|
||||
til::latch latch{ 1 };
|
||||
|
||||
std::ignore = [&]() -> winrt::fire_and_forget {
|
||||
const auto cleanup = wil::scope_exit([&]() {
|
||||
latch.count_down();
|
||||
});
|
||||
co_await winrt::resume_background();
|
||||
result = DefaultTerminal::Available();
|
||||
}();
|
||||
|
||||
latch.wait();
|
||||
_defaultTerminals = winrt::single_threaded_observable_vector(std::move(result.first));
|
||||
_currentDefaultTerminal = std::move(result.second);
|
||||
}
|
||||
|
||||
void CascadiaSettings::ExportFile(winrt::hstring path, winrt::hstring content)
|
||||
|
||||
@@ -61,9 +61,11 @@ static auto extractValueFromTaskWithoutMainThreadAwait(TTask&& task) -> decltype
|
||||
til::latch latch{ 1 };
|
||||
|
||||
const auto _ = [&]() -> winrt::fire_and_forget {
|
||||
const auto cleanup = wil::scope_exit([&]() {
|
||||
latch.count_down();
|
||||
});
|
||||
co_await winrt::resume_background();
|
||||
finalVal.emplace(co_await task);
|
||||
latch.count_down();
|
||||
}();
|
||||
|
||||
latch.wait();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalVisualStudioSetup>true</TerminalVisualStudioSetup>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
@@ -274,13 +275,6 @@
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
<!-- This target will take our defaults.json and stamp it into a .h file that
|
||||
we can include in the code directly. This way, we don't need to worry about
|
||||
failing to load the default settings at runtime. -->
|
||||
|
||||
@@ -299,16 +299,19 @@
|
||||
<value>Next tab</value>
|
||||
</data>
|
||||
<data name="OpenBothSettingsFilesCommandKey" xml:space="preserve">
|
||||
<value>Open both settings and default settings files</value>
|
||||
<value>Open both settings and default settings files (JSON)</value>
|
||||
<comment>{Locked="JSON"}. "JSON" is the extension of the file that will be opened.</comment>
|
||||
</data>
|
||||
<data name="OpenDefaultSettingsCommandKey" xml:space="preserve">
|
||||
<value>Open default settings file</value>
|
||||
<value>Open default settings file (JSON)</value>
|
||||
<comment>{Locked="JSON"}. "JSON" is the extension of the file that will be opened.</comment>
|
||||
</data>
|
||||
<data name="OpenNewTabDropdownCommandKey" xml:space="preserve">
|
||||
<value>Open new tab dropdown</value>
|
||||
</data>
|
||||
<data name="OpenSettingsCommandKey" xml:space="preserve">
|
||||
<value>Open settings file</value>
|
||||
<value>Open settings file (JSON)</value>
|
||||
<comment>{Locked="JSON"}. "JSON" is the extension of the file that will be opened.</comment>
|
||||
</data>
|
||||
<data name="OpenTabColorPickerCommandKey" xml:space="preserve">
|
||||
<value>Set the tab color...</value>
|
||||
|
||||
@@ -50,6 +50,25 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
return { horizAlign, vertAlign };
|
||||
}
|
||||
|
||||
winrt::com_ptr<implementation::TerminalSettings> TerminalSettings::_CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile)
|
||||
{
|
||||
auto settings{ winrt::make_self<TerminalSettings>() };
|
||||
|
||||
const auto globals = appSettings.GlobalSettings();
|
||||
settings->_ApplyProfileSettings(profile);
|
||||
settings->_ApplyGlobalSettings(globals);
|
||||
settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes());
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
Model::TerminalSettings TerminalSettings::CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile)
|
||||
{
|
||||
const auto settings = _CreateWithProfileCommon(appSettings, profile);
|
||||
settings->UseBackgroundImageForWindow(false);
|
||||
return *settings;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create a TerminalSettingsCreateResult for the provided profile guid. We'll
|
||||
// use the guid to look up the profile that should be used to
|
||||
@@ -64,17 +83,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// one for when the terminal is focused and the other for when the terminal is unfocused
|
||||
Model::TerminalSettingsCreateResult TerminalSettings::CreateWithProfile(const Model::CascadiaSettings& appSettings, const Model::Profile& profile, const IKeyBindings& keybindings)
|
||||
{
|
||||
auto settings{ winrt::make_self<TerminalSettings>() };
|
||||
const auto settings = _CreateWithProfileCommon(appSettings, profile);
|
||||
settings->_KeyBindings = keybindings;
|
||||
|
||||
const auto globals = appSettings.GlobalSettings();
|
||||
settings->_ApplyProfileSettings(profile);
|
||||
settings->_ApplyGlobalSettings(globals);
|
||||
settings->_ApplyAppearanceSettings(profile.DefaultAppearance(), globals.ColorSchemes());
|
||||
|
||||
Model::TerminalSettings child{ nullptr };
|
||||
if (const auto& unfocusedAppearance{ profile.UnfocusedAppearance() })
|
||||
{
|
||||
const auto globals = appSettings.GlobalSettings();
|
||||
auto childImpl = settings->CreateChild();
|
||||
childImpl->_ApplyAppearanceSettings(unfocusedAppearance, globals.ColorSchemes());
|
||||
child = *childImpl;
|
||||
|
||||
@@ -55,6 +55,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{
|
||||
TerminalSettings() = default;
|
||||
|
||||
static Model::TerminalSettings CreateForPreview(const Model::CascadiaSettings& appSettings, const Model::Profile& profile);
|
||||
|
||||
static Model::TerminalSettingsCreateResult CreateWithProfile(const Model::CascadiaSettings& appSettings,
|
||||
const Model::Profile& profile,
|
||||
const Control::IKeyBindings& keybindings);
|
||||
@@ -159,6 +161,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
private:
|
||||
std::optional<std::array<Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE>> _ColorTable;
|
||||
gsl::span<Microsoft::Terminal::Core::Color> _getColorTableImpl();
|
||||
|
||||
static winrt::com_ptr<implementation::TerminalSettings> _CreateWithProfileCommon(const Model::CascadiaSettings& appSettings, const Model::Profile& profile);
|
||||
void _ApplyProfileSettings(const Model::Profile& profile);
|
||||
|
||||
void _ApplyGlobalSettings(const Model::GlobalAppSettings& globalSettings) noexcept;
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
{
|
||||
TerminalSettings();
|
||||
|
||||
static TerminalSettings CreateForPreview(CascadiaSettings appSettings, Profile profile);
|
||||
static TerminalSettingsCreateResult CreateWithProfile(CascadiaSettings appSettings, Profile profile, Microsoft.Terminal.Control.IKeyBindings keybindings);
|
||||
static TerminalSettingsCreateResult CreateWithNewTerminalArgs(CascadiaSettings appSettings, NewTerminalArgs newTerminalArgs, Microsoft.Terminal.Control.IKeyBindings keybindings);
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#include "LegacyProfileGeneratorNamespaces.h"
|
||||
#include "../../inc/DefaultSettings.h"
|
||||
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include "DynamicProfileUtils.h"
|
||||
|
||||
static constexpr std::wstring_view WslHomeDirectory{ L"~" };
|
||||
@@ -68,105 +66,6 @@ static winrt::com_ptr<implementation::Profile> makeProfile(const std::wstring& d
|
||||
return WSLDistro;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Enumerates all the installed WSL distros to create profiles for them.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a vector with all distros for all the installed WSL distros
|
||||
static void legacyGenerate(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
|
||||
{
|
||||
wil::unique_handle readPipe;
|
||||
wil::unique_handle writePipe;
|
||||
SECURITY_ATTRIBUTES sa{ sizeof(sa), nullptr, true };
|
||||
THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&readPipe, &writePipe, &sa, 0));
|
||||
STARTUPINFO si{ 0 };
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = writePipe.get();
|
||||
si.hStdError = writePipe.get();
|
||||
wil::unique_process_information pi;
|
||||
wil::unique_cotaskmem_string systemPath;
|
||||
THROW_IF_FAILED(wil::GetSystemDirectoryW(systemPath));
|
||||
std::wstring command(systemPath.get());
|
||||
command += L"\\wsl.exe --list";
|
||||
|
||||
THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(nullptr,
|
||||
const_cast<LPWSTR>(command.c_str()),
|
||||
nullptr,
|
||||
nullptr,
|
||||
TRUE,
|
||||
CREATE_NO_WINDOW,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&si,
|
||||
&pi));
|
||||
switch (WaitForSingleObject(pi.hProcess, 2000))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_ABANDONED:
|
||||
case WAIT_TIMEOUT:
|
||||
return;
|
||||
case WAIT_FAILED:
|
||||
THROW_LAST_ERROR();
|
||||
default:
|
||||
THROW_HR(ERROR_UNHANDLED_EXCEPTION);
|
||||
}
|
||||
DWORD exitCode;
|
||||
if (!GetExitCodeProcess(pi.hProcess, &exitCode))
|
||||
{
|
||||
THROW_HR(E_INVALIDARG);
|
||||
}
|
||||
else if (exitCode != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DWORD bytesAvailable;
|
||||
THROW_IF_WIN32_BOOL_FALSE(PeekNamedPipe(readPipe.get(), nullptr, NULL, nullptr, &bytesAvailable, nullptr));
|
||||
// "The _open_osfhandle call transfers ownership of the Win32 file handle to the file descriptor."
|
||||
// (https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open-osfhandle?view=vs-2019)
|
||||
// so, we detach_from_smart_pointer it -- but...
|
||||
// "File descriptors passed into _fdopen are owned by the returned FILE * stream.
|
||||
// If _fdopen is successful, do not call _close on the file descriptor.
|
||||
// Calling fclose on the returned FILE * also closes the file descriptor."
|
||||
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/fdopen-wfdopen?view=vs-2019
|
||||
auto stdioPipeHandle = _wfdopen(_open_osfhandle((intptr_t)wil::detach_from_smart_pointer(readPipe), _O_WTEXT | _O_RDONLY), L"r");
|
||||
auto closeFile = wil::scope_exit([&]() { fclose(stdioPipeHandle); });
|
||||
|
||||
std::wfstream pipe{ stdioPipeHandle };
|
||||
|
||||
std::wstring wline;
|
||||
std::getline(pipe, wline); // remove the header from the output.
|
||||
while (pipe.tellp() < bytesAvailable)
|
||||
{
|
||||
std::getline(pipe, wline);
|
||||
std::wstringstream wlinestream(wline);
|
||||
if (wlinestream)
|
||||
{
|
||||
std::wstring distName;
|
||||
std::getline(wlinestream, distName, L'\r');
|
||||
|
||||
if (til::starts_with(distName, DockerDistributionPrefix))
|
||||
{
|
||||
// Docker for Windows creates some utility distributions to handle Docker commands.
|
||||
// Pursuant to GH#3556, because they are _not_ user-facing we want to hide them.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto firstChar = distName.find_first_of(L"( ");
|
||||
// Some localizations don't have a space between the name and "(Default)"
|
||||
// https://github.com/microsoft/terminal/issues/1168#issuecomment-500187109
|
||||
if (firstChar < distName.size())
|
||||
{
|
||||
distName.resize(firstChar);
|
||||
}
|
||||
|
||||
profiles.emplace_back(makeProfile(distName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Create a list of Profiles for each distro listed in names.
|
||||
// - Skips distros that are utility distros for docker (see GH#3556)
|
||||
@@ -310,9 +209,9 @@ static bool getWslNames(const wil::unique_hkey& wslRootKey,
|
||||
// Method Description:
|
||||
// - Generate a list of profiles for each on the installed WSL distros. This
|
||||
// will first try to read the installed distros from the registry. If that
|
||||
// fails, we'll fall back to the legacy way of launching WSL.exe to read the
|
||||
// distros from the commandline. Reading the registry is slightly more stable
|
||||
// (see GH#7199, GH#9905), but it is certainly BODGY
|
||||
// fails, we'll assume there are no WSL distributions installed.
|
||||
// Reading the registry is slightly more stable (see GH#7199, GH#9905),
|
||||
// but it is certainly BODGY
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
@@ -333,6 +232,4 @@ void WslDistroGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
legacyGenerate(profiles);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="NuGet Dependencies">
|
||||
<TerminalCppWinrt>true</TerminalCppWinrt>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
|
||||
@@ -105,14 +106,6 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
|
||||
@@ -225,8 +225,13 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
|
||||
TEST_METHOD(SimpleAltBufferTest);
|
||||
TEST_METHOD(AltBufferToAltBufferTest);
|
||||
|
||||
TEST_METHOD(TestPowerLineFirstFrame);
|
||||
|
||||
TEST_METHOD(AltBufferResizeCrash);
|
||||
|
||||
TEST_METHOD(TestNoExtendedAttrsOptimization);
|
||||
TEST_METHOD(TestNoBackgroundAttrsOptimization);
|
||||
|
||||
private:
|
||||
bool _writeCallback(const char* const pch, const size_t cch);
|
||||
void _flushFirstFrame();
|
||||
@@ -4076,12 +4081,88 @@ void ConptyRoundtripTests::AltBufferToAltBufferTest()
|
||||
verifyBuffer(*termAltTb, til::rect{ term->_GetMutableViewport().ToInclusive() }, Frame::StillInAltBuffer);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::TestPowerLineFirstFrame()
|
||||
{
|
||||
Log::Comment(L"This is a test for GH#8341. If we received colored spaces "
|
||||
L"BEFORE the first frame, we should still emit them!");
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
|
||||
auto* hostTb = &si.GetTextBuffer();
|
||||
auto* termTb = term->_mainBuffer.get();
|
||||
|
||||
_checkConptyOutput = false;
|
||||
|
||||
TextAttribute whiteOnGreen{};
|
||||
whiteOnGreen.SetIndexedForeground(TextColor::DARK_WHITE);
|
||||
whiteOnGreen.SetIndexedBackground(TextColor::BRIGHT_GREEN);
|
||||
|
||||
TextAttribute greenOnBlack{};
|
||||
greenOnBlack.SetIndexedForeground(TextColor::BRIGHT_GREEN);
|
||||
greenOnBlack.SetIndexedBackground(TextColor::BRIGHT_BLACK);
|
||||
|
||||
TextAttribute whiteOnBlack{};
|
||||
whiteOnBlack.SetIndexedForeground(TextColor::DARK_WHITE);
|
||||
whiteOnBlack.SetIndexedBackground(TextColor::BRIGHT_BLACK);
|
||||
|
||||
TextAttribute blackOnDefault{};
|
||||
blackOnDefault.SetIndexedForeground(TextColor::BRIGHT_BLACK);
|
||||
|
||||
TextAttribute defaultOnDefault{};
|
||||
|
||||
Log::Comment(L"========== Fill test content ==========");
|
||||
|
||||
// As a pwsh one-liner:
|
||||
//
|
||||
// "`e[37m`e[102m foo\bar `e[92m`e[100m▶ `e[37mBar `e[90m`e[49m▶ `e[m"
|
||||
//
|
||||
// Generally taken from
|
||||
// https://github.com/microsoft/terminal/issues/8341#issuecomment-731310022,
|
||||
// but minimized for easier testing.
|
||||
|
||||
sm.ProcessString(L"\x1b[37m\x1b[102m" // dark white on bright green
|
||||
L" foo\\bar ");
|
||||
sm.ProcessString(L"\x1b[92m\x1b[100m" // bright green on bright black
|
||||
L"▶ ");
|
||||
sm.ProcessString(L"\x1b[37m" // dark white on bright black
|
||||
L"Bar ");
|
||||
sm.ProcessString(L"\x1b[90m\x1b[49m" // bright black on default
|
||||
L"▶ ");
|
||||
sm.ProcessString(L"\x1b[m\n"); // default on default
|
||||
|
||||
auto verifyBuffer = [&](const TextBuffer& tb) {
|
||||
// If this test fails on character 8, then it's because we didn't emit the space, we just moved ahead.
|
||||
auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, whiteOnGreen, 9u);
|
||||
TestUtils::VerifyLineContains(iter0, OutputCellIterator{ greenOnBlack, 2u });
|
||||
TestUtils::VerifyLineContains(iter0, OutputCellIterator{ whiteOnBlack, 4u });
|
||||
TestUtils::VerifyLineContains(iter0, OutputCellIterator{ blackOnDefault, 2u });
|
||||
};
|
||||
|
||||
Log::Comment(L"========== Check host buffer ==========");
|
||||
verifyBuffer(*hostTb);
|
||||
|
||||
Log::Comment(L"========== Paint first frame ==========");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"========== Check terminal buffer ==========");
|
||||
verifyBuffer(*termTb);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::AltBufferResizeCrash()
|
||||
{
|
||||
Log::Comment(L"During the review for GH#12719, it was noticed that this "
|
||||
L"particular combination of resizing could crash the terminal."
|
||||
L" This test makes sure we don't.");
|
||||
|
||||
// Anything that resizes the buffer needs IsolationLevel:Method
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
@@ -4122,3 +4203,99 @@ void ConptyRoundtripTests::AltBufferResizeCrash()
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::TestNoExtendedAttrsOptimization()
|
||||
{
|
||||
Log::Comment(L"We don't want conpty to optimize out runs of spaces that DO "
|
||||
L"have extended attrs, because EL / ECH don't fill space with "
|
||||
L"those attributes");
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
|
||||
auto* hostTb = &si.GetTextBuffer();
|
||||
auto* termTb = term->_mainBuffer.get();
|
||||
|
||||
gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state.
|
||||
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
_checkConptyOutput = false;
|
||||
|
||||
TextAttribute reverseAttrs{};
|
||||
reverseAttrs.SetReverseVideo(true);
|
||||
|
||||
auto verifyBuffer = [&](const TextBuffer& tb) {
|
||||
auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L' ', reverseAttrs, 9u);
|
||||
TestUtils::VerifyExpectedString(L"test", iter0);
|
||||
TestUtils::VerifyLineContains(iter0, L' ', reverseAttrs, 9u);
|
||||
|
||||
TestUtils::VerifyLineContains(tb, { 0, 1 }, L' ', reverseAttrs, static_cast<uint32_t>(TerminalViewWidth));
|
||||
};
|
||||
|
||||
Log::Comment(L"========== Fill test content ==========");
|
||||
sm.ProcessString(L"\x1b[7m test \x1b[m\n");
|
||||
sm.ProcessString(L"\x1b[7m");
|
||||
sm.ProcessString(std::wstring(TerminalViewWidth, L' '));
|
||||
sm.ProcessString(L"\x1b[m\n");
|
||||
|
||||
Log::Comment(L"========== Check host buffer ==========");
|
||||
verifyBuffer(*hostTb);
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"========== Check terminal buffer ==========");
|
||||
verifyBuffer(*termTb);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::TestNoBackgroundAttrsOptimization()
|
||||
{
|
||||
Log::Comment(L"Same as above, with BG attrs");
|
||||
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
|
||||
auto* hostTb = &si.GetTextBuffer();
|
||||
auto* termTb = term->_mainBuffer.get();
|
||||
|
||||
gci.LockConsole(); // Lock must be taken to manipulate alt/main buffer state.
|
||||
auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); });
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
_checkConptyOutput = false;
|
||||
|
||||
TextAttribute bgAttrs{};
|
||||
bgAttrs.SetIndexedBackground(TextColor::DARK_WHITE);
|
||||
|
||||
auto verifyBuffer = [&](const TextBuffer& tb) {
|
||||
auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L' ', bgAttrs, 9u);
|
||||
TestUtils::VerifyExpectedString(L"test", iter0);
|
||||
TestUtils::VerifyLineContains(iter0, L' ', bgAttrs, 9u);
|
||||
|
||||
TestUtils::VerifyLineContains(tb, { 0, 1 }, L' ', bgAttrs, static_cast<uint32_t>(TerminalViewWidth));
|
||||
};
|
||||
|
||||
Log::Comment(L"========== Fill test content ==========");
|
||||
sm.ProcessString(L"\x1b[47m test \x1b[m\n");
|
||||
sm.ProcessString(L"\x1b[47m");
|
||||
sm.ProcessString(std::wstring(TerminalViewWidth, L' '));
|
||||
sm.ProcessString(L"\x1b[m\n");
|
||||
|
||||
Log::Comment(L"========== Check host buffer ==========");
|
||||
verifyBuffer(*hostTb);
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
|
||||
Log::Comment(L"========== Check terminal buffer ==========");
|
||||
verifyBuffer(*termTb);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,8 @@ AppHost::AppHost() noexcept :
|
||||
auto pfn = std::bind(&AppHost::_HandleCreateWindow,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3);
|
||||
_window->SetCreateCallback(pfn);
|
||||
|
||||
_window->SetSnapDimensionCallback(std::bind(&winrt::TerminalApp::AppLogic::CalcSnappedDimension,
|
||||
@@ -116,6 +117,19 @@ AppHost::AppHost() noexcept :
|
||||
{
|
||||
_BecomeMonarch(nullptr, nullptr);
|
||||
}
|
||||
|
||||
// Create a throttled function for updating the window state, to match the
|
||||
// one requested by the pty. A 200ms delay was chosen because it's the
|
||||
// typical animation timeout in Windows. This does result in a delay between
|
||||
// the PTY requesting a change to the window state and the Terminal
|
||||
// realizing it, but should mitigate issues where the Terminal and PTY get
|
||||
// de-sync'd.
|
||||
_showHideWindowThrottler = std::make_shared<ThrottledFuncTrailing<bool>>(
|
||||
winrt::Windows::System::DispatcherQueue::GetForCurrentThread(),
|
||||
std::chrono::milliseconds(200),
|
||||
[this](const bool show) {
|
||||
_window->ShowWindowChanged(show);
|
||||
});
|
||||
}
|
||||
|
||||
AppHost::~AppHost()
|
||||
@@ -128,6 +142,8 @@ AppHost::~AppHost()
|
||||
// out the window, then close the app.
|
||||
_revokers = {};
|
||||
|
||||
_showHideWindowThrottler.reset();
|
||||
|
||||
_window = nullptr;
|
||||
_app.Close();
|
||||
_app = nullptr;
|
||||
@@ -378,7 +394,6 @@ void AppHost::Initialize()
|
||||
_revokers.FullscreenChanged = _logic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged });
|
||||
_revokers.FocusModeChanged = _logic.FocusModeChanged(winrt::auto_revoke, { this, &AppHost::_FocusModeChanged });
|
||||
_revokers.AlwaysOnTopChanged = _logic.AlwaysOnTopChanged(winrt::auto_revoke, { this, &AppHost::_AlwaysOnTopChanged });
|
||||
_revokers.Initialized = _logic.Initialized(winrt::auto_revoke, { this, &AppHost::_AppInitializedHandler });
|
||||
_revokers.RaiseVisualBell = _logic.RaiseVisualBell(winrt::auto_revoke, { this, &AppHost::_RaiseVisualBell });
|
||||
_revokers.SystemMenuChangeRequested = _logic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested });
|
||||
_revokers.ChangeMaximizeRequested = _logic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested });
|
||||
@@ -520,31 +535,12 @@ LaunchPosition AppHost::_GetWindowLaunchPosition()
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Callback for when the window is first being created (during WM_CREATE).
|
||||
// Stash the proposed size for later. We'll need that once we're totally
|
||||
// initialized, so that we can show the window in the right position *when we
|
||||
// want to show it*. If we did the _initialResizeAndRepositionWindow work now,
|
||||
// it would have no effect, because the window is not yet visible.
|
||||
// Arguments:
|
||||
// - hwnd: The HWND of the window we're about to create.
|
||||
// - proposedRect: The location and size of the window that we're about to
|
||||
// create. We'll use this rect to determine which monitor the window is about
|
||||
// to appear on.
|
||||
void AppHost::_HandleCreateWindow(const HWND /* hwnd */, RECT proposedRect)
|
||||
{
|
||||
// GH#11561: Hide the window until we're totally done being initialized.
|
||||
// More commentary in TerminalPage::_CompleteInitialization
|
||||
_proposedRect = proposedRect;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Resize the window we're about to create to the appropriate dimensions, as
|
||||
// specified in the settings. This is called once the app has finished it's
|
||||
// initial setup, once we have created all the tabs, panes, etc. We'll load
|
||||
// the settings for the app, then get the proposed size of the terminal from
|
||||
// the app. Using that proposed size, we'll resize the window we're creating,
|
||||
// so that it'll match the values in the settings.
|
||||
// specified in the settings. This will be called during the handling of
|
||||
// WM_CREATE. We'll load the settings for the app, then get the proposed size
|
||||
// of the terminal from the app. Using that proposed size, we'll resize the
|
||||
// window we're creating, so that it'll match the values in the settings.
|
||||
// Arguments:
|
||||
// - hwnd: The HWND of the window we're about to create.
|
||||
// - proposedRect: The location and size of the window that we're about to
|
||||
@@ -553,7 +549,7 @@ void AppHost::_HandleCreateWindow(const HWND /* hwnd */, RECT proposedRect)
|
||||
// - launchMode: A LaunchMode enum reference that indicates the launch mode
|
||||
// Return Value:
|
||||
// - None
|
||||
void AppHost::_initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, LaunchMode& launchMode)
|
||||
void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode& launchMode)
|
||||
{
|
||||
launchMode = _logic.GetLaunchMode();
|
||||
|
||||
@@ -1416,7 +1412,14 @@ void AppHost::_QuitAllRequested(const winrt::Windows::Foundation::IInspectable&,
|
||||
void AppHost::_ShowWindowChanged(const winrt::Windows::Foundation::IInspectable&,
|
||||
const winrt::Microsoft::Terminal::Control::ShowWindowArgs& args)
|
||||
{
|
||||
_window->ShowWindowChanged(args.ShowOrHide());
|
||||
// GH#13147: Enqueue a throttled update to our window state. Throttling
|
||||
// should prevent scenarios where the Terminal window state and PTY window
|
||||
// state get de-sync'd, and cause the window to minimize/restore constantly
|
||||
// in a loop.
|
||||
if (_showHideWindowThrottler)
|
||||
{
|
||||
_showHideWindowThrottler->Run(args.ShowOrHide());
|
||||
}
|
||||
}
|
||||
|
||||
void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
@@ -1575,22 +1578,3 @@ void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*
|
||||
const auto pos = _GetWindowLaunchPosition();
|
||||
_logic.CloseWindow(pos);
|
||||
}
|
||||
|
||||
void AppHost::_AppInitializedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*arg*/)
|
||||
{
|
||||
// GH#11561: We're totally done being initialized. Resize the window to
|
||||
// match the initial settings, and then call ShowWindow to finally make us
|
||||
// visible.
|
||||
|
||||
LaunchMode launchMode{};
|
||||
_initialResizeAndRepositionWindow(_window->GetHandle(), _proposedRect, launchMode);
|
||||
|
||||
auto nCmdShow = SW_SHOWDEFAULT;
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode))
|
||||
{
|
||||
nCmdShow = SW_MAXIMIZE;
|
||||
}
|
||||
|
||||
ShowWindow(_window->GetHandle(), nCmdShow);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "pch.h"
|
||||
#include "NonClientIslandWindow.h"
|
||||
#include "NotificationIcon.h"
|
||||
#include <til/throttled_func.h>
|
||||
#include <ThrottledFunc.h>
|
||||
|
||||
class AppHost
|
||||
{
|
||||
@@ -31,16 +31,16 @@ private:
|
||||
|
||||
bool _shouldCreateWindow{ false };
|
||||
bool _useNonClientArea{ false };
|
||||
RECT _proposedRect{};
|
||||
|
||||
std::optional<til::throttled_func_trailing<>> _getWindowLayoutThrottler;
|
||||
std::shared_ptr<ThrottledFuncTrailing<bool>> _showHideWindowThrottler;
|
||||
winrt::Windows::Foundation::IAsyncAction _SaveWindowLayouts();
|
||||
winrt::fire_and_forget _SaveWindowLayoutsRepeat();
|
||||
|
||||
void _HandleCommandlineArgs();
|
||||
winrt::Microsoft::Terminal::Settings::Model::LaunchPosition _GetWindowLaunchPosition();
|
||||
|
||||
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect);
|
||||
void _HandleCreateWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
|
||||
void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::UI::Xaml::UIElement& arg);
|
||||
void _UpdateTheme(const winrt::Windows::Foundation::IInspectable&,
|
||||
@@ -53,9 +53,6 @@ private:
|
||||
const winrt::Windows::Foundation::IInspectable& arg);
|
||||
void _AlwaysOnTopChanged(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& arg);
|
||||
void _AppInitializedHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& arg);
|
||||
|
||||
void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& arg);
|
||||
void _WindowMouseWheeled(const til::point coord, const int32_t delta);
|
||||
@@ -125,9 +122,6 @@ private:
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
void _HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
void _initialResizeAndRepositionWindow(const HWND hwnd, RECT proposedRect, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode);
|
||||
|
||||
std::unique_ptr<NotificationIcon> _notificationIcon;
|
||||
winrt::event_token _ReAddNotificationIconToken;
|
||||
winrt::event_token _NotificationIconPressedToken;
|
||||
@@ -154,7 +148,6 @@ private:
|
||||
winrt::TerminalApp::AppLogic::FullscreenChanged_revoker FullscreenChanged;
|
||||
winrt::TerminalApp::AppLogic::FocusModeChanged_revoker FocusModeChanged;
|
||||
winrt::TerminalApp::AppLogic::AlwaysOnTopChanged_revoker AlwaysOnTopChanged;
|
||||
winrt::TerminalApp::AppLogic::Initialized_revoker Initialized;
|
||||
winrt::TerminalApp::AppLogic::RaiseVisualBell_revoker RaiseVisualBell;
|
||||
winrt::TerminalApp::AppLogic::SystemMenuChangeRequested_revoker SystemMenuChangeRequested;
|
||||
winrt::TerminalApp::AppLogic::ChangeMaximizeRequested_revoker ChangeMaximizeRequested;
|
||||
|
||||
@@ -112,7 +112,7 @@ void IslandWindow::Close()
|
||||
// window.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void IslandWindow::SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept
|
||||
void IslandWindow::SetCreateCallback(std::function<void(const HWND, const RECT, LaunchMode& launchMode)> pfn) noexcept
|
||||
{
|
||||
_pfnCreateCallback = pfn;
|
||||
}
|
||||
@@ -154,13 +154,19 @@ void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexce
|
||||
rc.right = rc.left + pcs->cx;
|
||||
rc.bottom = rc.top + pcs->cy;
|
||||
|
||||
auto launchMode = LaunchMode::DefaultMode;
|
||||
if (_pfnCreateCallback)
|
||||
{
|
||||
_pfnCreateCallback(_window.get(), rc);
|
||||
_pfnCreateCallback(_window.get(), rc, launchMode);
|
||||
}
|
||||
|
||||
// GH#11561: DO NOT call ShowWindow here. The AppHost will call ShowWindow
|
||||
// once the app has completed its initialization.
|
||||
auto nCmdShow = SW_SHOW;
|
||||
if (WI_IsFlagSet(launchMode, LaunchMode::MaximizedMode))
|
||||
{
|
||||
nCmdShow = SW_MAXIMIZE;
|
||||
}
|
||||
|
||||
ShowWindow(_window.get(), nCmdShow);
|
||||
|
||||
UpdateWindow(_window.get());
|
||||
|
||||
@@ -828,10 +834,20 @@ void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop)
|
||||
// - <none>
|
||||
void IslandWindow::ShowWindowChanged(const bool showOrHide)
|
||||
{
|
||||
const auto hwnd = GetHandle();
|
||||
if (hwnd)
|
||||
if (const auto hwnd = GetHandle())
|
||||
{
|
||||
PostMessage(hwnd, WM_SYSCOMMAND, showOrHide ? SC_RESTORE : SC_MINIMIZE, 0);
|
||||
// IMPORTANT!
|
||||
//
|
||||
// ONLY "restore" if already minimized. If the window is maximized or
|
||||
// snapped, a restore will restore-down the window instead.
|
||||
if (showOrHide == true && ::IsIconic(hwnd))
|
||||
{
|
||||
::PostMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
|
||||
}
|
||||
else if (showOrHide == false)
|
||||
{
|
||||
::PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
void SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept;
|
||||
void SetCreateCallback(std::function<void(const HWND, const RECT, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode)> pfn) noexcept;
|
||||
void SetSnapDimensionCallback(std::function<float(bool widthOrHeight, float dimension)> pfn) noexcept;
|
||||
|
||||
void FocusModeChanged(const bool focusMode);
|
||||
@@ -94,7 +94,7 @@ protected:
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _rootGrid;
|
||||
wil::com_ptr<ITaskbarList3> _taskbar;
|
||||
|
||||
std::function<void(const HWND, const RECT)> _pfnCreateCallback;
|
||||
std::function<void(const HWND, const RECT, winrt::Microsoft::Terminal::Settings::Model::LaunchMode& launchMode)> _pfnCreateCallback;
|
||||
std::function<float(bool, float)> _pfnSnapDimensionCallback;
|
||||
|
||||
void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<TerminalXamlApplicationToolkit>true</TerminalXamlApplicationToolkit>
|
||||
<TerminalVCRTForwarders>true</TerminalVCRTForwarders>
|
||||
<TerminalThemeHelpers>true</TerminalThemeHelpers>
|
||||
<TerminalMUX>true</TerminalMUX>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||||
@@ -141,14 +142,6 @@
|
||||
|
||||
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||||
|
||||
<Import Project="..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
</Target>
|
||||
|
||||
<!-- Override GetPackagingOutputs to roll up all our dependencies.
|
||||
This ensures that when the WAP packaging project asks what files go into
|
||||
the package, we tell it.
|
||||
|
||||
@@ -155,13 +155,13 @@ namespace WindowsTerminal.UIA.Tests
|
||||
{
|
||||
var root = app.GetRoot();
|
||||
|
||||
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
|
||||
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
|
||||
Globals.WaitForTimeout();
|
||||
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
|
||||
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
|
||||
Globals.WaitForTimeout();
|
||||
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
|
||||
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
|
||||
Globals.WaitForTimeout();
|
||||
root.SendKeys(Keys.LeftAlt + Keys.LeftShift + "T");
|
||||
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "T");
|
||||
Globals.WaitForTimeout();
|
||||
root.SendKeys(Keys.LeftControl + Keys.LeftShift + "W");
|
||||
Globals.WaitForTimeout();
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<Reference Include="Castle.Core, Version=4.1.1.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>$(CastleCorePathRoot)\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(NewtonsoftJSONPathRoot)\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
<!-- NewtonsoftJSON -->
|
||||
<PropertyGroup>
|
||||
<NewtonsoftJSONPathRoot>$(MSBuildThisFileDirectory)..\packages\Newtonsoft.Json.12.0.3</NewtonsoftJSONPathRoot>
|
||||
<NewtonsoftJSONPathRoot>$(MSBuildThisFileDirectory)..\packages\Newtonsoft.Json.13.0.1</NewtonsoftJSONPathRoot>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- SeleniumWebDriver -->
|
||||
|
||||
@@ -58,6 +58,9 @@
|
||||
<!-- VisualStudioSetup -->
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="'$(TerminalVisualStudioSetup)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" />
|
||||
|
||||
<!-- WinUI (which depends on WebView2 as of 2.8.0) -->
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets" Condition="'$(TerminalMUX)' == 'true' and Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" />
|
||||
|
||||
<!-- WIL (so widely used that this one does not have a TerminalWIL opt-in property; it is automatic) -->
|
||||
<Import Project="$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
|
||||
@@ -95,6 +98,9 @@
|
||||
<!-- VisualStudioSetup -->
|
||||
<Error Condition="'$(TerminalVisualStudioSetup)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.2.3.2262\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets'))" />
|
||||
|
||||
<!-- WinUI (which depends on WebView2 as of 2.8.0) -->
|
||||
<Error Condition="'$(TerminalMUX)' == 'true' AND !Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\build\native\Microsoft.UI.Xaml.targets'))" />
|
||||
|
||||
<!-- WIL (so widely used that this one does not have a TerminalWIL opt-in property; it is automatic) -->
|
||||
<Error Condition="!Exists('$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(MSBuildThisFileDirectory)..\packages\Microsoft.Windows.ImplementationLibrary.1.0.220201.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature";
|
||||
const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty";
|
||||
const std::wstring_view ConsoleArguments::COM_SERVER_ARG = L"-Embedding";
|
||||
const std::wstring_view ConsoleArguments::PASSTHROUGH_ARG = L"--passthrough";
|
||||
// NOTE: Thinking about adding more commandline args that control conpty, for
|
||||
// the Terminal? Make sure you add them to the commandline in
|
||||
// ConsoleEstablishHandoff. We use that to initialize the ConsoleArguments for a
|
||||
// defterm handoff s.t. they behave like a conpty connection that was started by
|
||||
// the terminal. If you forget them there, the conpty won't obey them, only for
|
||||
// defterm.
|
||||
|
||||
std::wstring EscapeArgument(std::wstring_view ac)
|
||||
{
|
||||
|
||||
@@ -75,11 +75,20 @@ void PtySignalInputThread::ConnectConsole() noexcept
|
||||
_DoShowHide(_initialShowHide->show);
|
||||
}
|
||||
|
||||
// If we were given a owner HWND, then manually start the pseudo window now.
|
||||
if (_earlyReparent)
|
||||
{
|
||||
_DoSetWindowParent(*_earlyReparent);
|
||||
}
|
||||
// We should have successfully used the _earlyReparent message in CreatePseudoWindow.
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create our pseudo window. We're doing this here, instead of in
|
||||
// ConnectConsole, because the window is created in
|
||||
// ConsoleInputThreadProcWin32, before ConnectConsole is first called. Doing
|
||||
// this here ensures that the window is first created with the initial owner
|
||||
// set up (if so specified).
|
||||
// - Refer to GH#13066 for details.
|
||||
void PtySignalInputThread::CreatePseudoWindow()
|
||||
{
|
||||
HWND owner = _earlyReparent.has_value() ? reinterpret_cast<HWND>((*_earlyReparent).handle) : HWND_DESKTOP;
|
||||
ServiceLocator::LocatePseudoWindow(owner);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -227,15 +236,28 @@ void PtySignalInputThread::_DoShowHide(const bool show)
|
||||
// - <none>
|
||||
void PtySignalInputThread::_DoSetWindowParent(const SetParentData& data)
|
||||
{
|
||||
const auto owner{ reinterpret_cast<HWND>(data.handle) };
|
||||
// This will initialize s_interactivityFactory for us. It will also
|
||||
// conveniently return 0 when we're on OneCore.
|
||||
//
|
||||
// If the window hasn't been created yet, by some other call to
|
||||
// LocatePseudoWindow, then this will also initialize the owner of the
|
||||
// window.
|
||||
if (const auto pseudoHwnd{ ServiceLocator::LocatePseudoWindow(reinterpret_cast<HWND>(data.handle)) })
|
||||
if (const auto pseudoHwnd{ ServiceLocator::LocatePseudoWindow(owner) })
|
||||
{
|
||||
LOG_LAST_ERROR_IF_NULL(::SetParent(pseudoHwnd, reinterpret_cast<HWND>(data.handle)));
|
||||
// DO NOT USE SetParent HERE!
|
||||
//
|
||||
// Calling SetParent on a window that is WS_VISIBLE will cause the OS to
|
||||
// hide the window, make it a _child_ window, then call SW_SHOW on the
|
||||
// window to re-show it. SW_SHOW, however, will cause the OS to also set
|
||||
// that window as the _foreground_ window, which would result in the
|
||||
// pty's hwnd stealing the foreground away from the owning terminal
|
||||
// window. That's bad.
|
||||
//
|
||||
// SetWindowLongPtr seems to do the job of changing who the window owner
|
||||
// is, without all the other side effects of reparenting the window.
|
||||
// See #13066
|
||||
::SetWindowLongPtr(pseudoHwnd, GWLP_HWNDPARENT, reinterpret_cast<LONG_PTR>(owner));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Microsoft::Console
|
||||
PtySignalInputThread& operator=(const PtySignalInputThread&) = delete;
|
||||
|
||||
void ConnectConsole() noexcept;
|
||||
void CreatePseudoWindow();
|
||||
|
||||
private:
|
||||
enum class PtySignal : unsigned short
|
||||
|
||||
@@ -300,18 +300,34 @@ bool VtIo::IsUsingVt() const
|
||||
|
||||
if (_pPtySignalInputThread)
|
||||
{
|
||||
// IMPORTANT! Start the pseudo window on this thread. This thread has a
|
||||
// message pump. If you DON'T, then a DPI change in the owning hwnd will
|
||||
// cause us to get a dpi change as well, which we'll never deque and
|
||||
// handle, effectively HANGING THE OWNER HWND.
|
||||
// Let the signal thread know that the console is connected.
|
||||
//
|
||||
// Let the signal thread know that the console is connected
|
||||
// By this point, the pseudo window should have already been created, by
|
||||
// ConsoleInputThreadProcWin32. That thread has a message pump, which is
|
||||
// needed to ensure that DPI change messages to the owning terminal
|
||||
// window don't end up hanging because the pty didn't also process it.
|
||||
_pPtySignalInputThread->ConnectConsole();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create our pseudo window. This is exclusively called by
|
||||
// ConsoleInputThreadProcWin32 on the console input thread.
|
||||
// * It needs to be called on that thread, before any other calls to
|
||||
// LocatePseudoWindow, to make sure that the input thread is the HWND's
|
||||
// message thread.
|
||||
// * It needs to be plumbed through the signal thread, because the signal
|
||||
// thread knows if someone should be marked as the window's owner. It's
|
||||
// VERY IMPORTANT that any initial owners are set up when the window is
|
||||
// first created.
|
||||
// - Refer to GH#13066 for details.
|
||||
void VtIo::CreatePseudoWindow()
|
||||
{
|
||||
_pPtySignalInputThread->CreatePseudoWindow();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Create and start the signal thread. The signal thread can be created
|
||||
// independent of the i/o threads, and doesn't require a client first
|
||||
|
||||
@@ -52,6 +52,8 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
|
||||
[[nodiscard]] HRESULT ManuallyClearScrollback() const noexcept;
|
||||
|
||||
void CreatePseudoWindow();
|
||||
|
||||
private:
|
||||
// After CreateIoHandlers is called, these will be invalid.
|
||||
wil::unique_hfile _hInput;
|
||||
|
||||
@@ -312,6 +312,9 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
||||
|
||||
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
||||
{
|
||||
// It's important that we flush the renderer at this point so we don't
|
||||
// have any pending output rendered after the scrollback is cleared.
|
||||
ServiceLocator::LocateGlobals().pRender->TriggerFlush(false);
|
||||
hr = gci.GetVtIo()->ManuallyClearScrollback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<Reference Include="Castle.Core, Version=4.1.1.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>$(CastleCorePathRoot)\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(NewtonsoftJSONPathRoot)\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
@@ -719,6 +719,10 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
|
||||
position.Y < 0));
|
||||
// clang-format on
|
||||
|
||||
// MSFT: 15813316 - Try to use this SetCursorPosition call to inherit the cursor position.
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(position));
|
||||
|
||||
RETURN_IF_NTSTATUS_FAILED(buffer.SetCursorPosition(position, true));
|
||||
|
||||
LOG_IF_FAILED(ConsoleImeResizeCompStrView());
|
||||
@@ -957,6 +961,9 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
|
||||
|
||||
if (sourceIsWholeBuffer && targetIsNegativeBufferHeight && noClipProvided && fillIsBlank)
|
||||
{
|
||||
// It's important that we flush the renderer at this point so we don't
|
||||
// have any pending output rendered after the scrollback is cleared.
|
||||
ServiceLocator::LocateGlobals().pRender->TriggerFlush(false);
|
||||
hr = gci.GetVtIo()->ManuallyClearScrollback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ void ProcessCtrlEvents()
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
Status = ServiceLocator::LocateConsoleControl()
|
||||
->EndTask((HANDLE)rgProcessHandleList[i].dwProcessID,
|
||||
->EndTask(rgProcessHandleList[i].dwProcessID,
|
||||
EventType,
|
||||
CtrlFlags);
|
||||
if (rgProcessHandleList[i].hProcess == nullptr)
|
||||
|
||||
@@ -285,7 +285,14 @@ void ConhostInternalGetSet::ShowWindow(bool showOrHide)
|
||||
{
|
||||
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
const auto hwnd = gci.IsInVtIoMode() ? ServiceLocator::LocatePseudoWindow() : ServiceLocator::LocateConsoleWindow()->GetWindowHandle();
|
||||
::ShowWindow(hwnd, showOrHide ? SW_NORMAL : SW_MINIMIZE);
|
||||
|
||||
// GH#13301 - When we send this ShowWindow message, if we send it to the
|
||||
// conhost HWND, it's going to need to get processed by the window message
|
||||
// thread before returning.
|
||||
// However, ShowWindowAsync doesn't have this problem. It'll post the
|
||||
// message to the window thread, then immediately return, so we don't have
|
||||
// to worry about deadlocking.
|
||||
::ShowWindowAsync(hwnd, showOrHide ? SW_SHOWNOACTIVATE : SW_MINIMIZE);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@@ -1453,6 +1453,8 @@ bool SCREEN_INFORMATION::IsMaximizedY() const
|
||||
|
||||
// Save cursor's relative height versus the viewport
|
||||
const auto sCursorHeightInViewportBefore = _textBuffer->GetCursor().GetPosition().Y - _viewport.Top();
|
||||
// Also save the distance to the virtual bottom so it can be restored after the resize
|
||||
const auto cursorDistanceFromBottom = _virtualBottom - _textBuffer->GetCursor().GetPosition().Y;
|
||||
|
||||
// skip any drawing updates that might occur until we swap _textBuffer with the new buffer or we exit early.
|
||||
newTextBuffer->GetCursor().StartDeferDrawing();
|
||||
@@ -1464,14 +1466,20 @@ bool SCREEN_INFORMATION::IsMaximizedY() const
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
// Make sure the new virtual bottom is far enough down to include both
|
||||
// the cursor row and the last non-space row. It also shouldn't be less
|
||||
// than the height of the viewport, otherwise the top of the virtual
|
||||
// viewport would end up negative.
|
||||
// Since the reflow doesn't preserve the virtual bottom, we try and
|
||||
// estimate where it ought to be by making it the same distance from
|
||||
// the cursor row as it was before the resize. However, we also need
|
||||
// to make sure it is far enough down to include the last non-space
|
||||
// row, and it shouldn't be less than the height of the viewport,
|
||||
// otherwise the top of the virtual viewport would end up negative.
|
||||
const auto cursorRow = newTextBuffer->GetCursor().GetPosition().Y;
|
||||
const auto lastNonSpaceRow = newTextBuffer->GetLastNonSpaceCharacter().Y;
|
||||
const auto estimatedBottom = gsl::narrow_cast<short>(cursorRow + cursorDistanceFromBottom);
|
||||
const auto viewportBottom = gsl::narrow_cast<short>(_viewport.Height() - 1);
|
||||
_virtualBottom = std::max({ cursorRow, lastNonSpaceRow, viewportBottom });
|
||||
_virtualBottom = std::max({ lastNonSpaceRow, estimatedBottom, viewportBottom });
|
||||
|
||||
// We can't let it extend past the bottom of the buffer either.
|
||||
_virtualBottom = std::min(_virtualBottom, newTextBuffer->GetSize().BottomInclusive());
|
||||
|
||||
// Adjust the viewport so the cursor doesn't wildly fly off up or down.
|
||||
const auto sCursorHeightInViewportAfter = cursorRow - _viewport.Top();
|
||||
@@ -2260,30 +2268,19 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport,
|
||||
}
|
||||
|
||||
// do adjustments on a copy that's easily manipulated.
|
||||
auto srCorrected = newViewport.ToExclusive();
|
||||
const til::rect viewportRect{ newViewport.ToInclusive() };
|
||||
const til::size coordScreenBufferSize{ GetBufferSize().Dimensions() };
|
||||
|
||||
if (srCorrected.Left < 0)
|
||||
{
|
||||
srCorrected.Right -= srCorrected.Left;
|
||||
srCorrected.Left = 0;
|
||||
}
|
||||
if (srCorrected.Top < 0)
|
||||
{
|
||||
srCorrected.Bottom -= srCorrected.Top;
|
||||
srCorrected.Top = 0;
|
||||
}
|
||||
// MSFT-33471786, GH#13193:
|
||||
// newViewport may reside anywhere outside of the valid coordScreenBufferSize.
|
||||
// For instance it might be scrolled down more than our text buffer allows to be scrolled.
|
||||
const auto cx = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.width(), 1, coordScreenBufferSize.width));
|
||||
const auto cy = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.height(), 1, coordScreenBufferSize.height));
|
||||
const auto x = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.left, 0, coordScreenBufferSize.width - cx));
|
||||
const auto y = gsl::narrow_cast<SHORT>(std::clamp(viewportRect.top, 0, coordScreenBufferSize.height - cy));
|
||||
|
||||
const auto coordScreenBufferSize = GetBufferSize().Dimensions();
|
||||
if (srCorrected.Right > coordScreenBufferSize.X)
|
||||
{
|
||||
srCorrected.Right = coordScreenBufferSize.X;
|
||||
}
|
||||
if (srCorrected.Bottom > coordScreenBufferSize.Y)
|
||||
{
|
||||
srCorrected.Bottom = coordScreenBufferSize.Y;
|
||||
}
|
||||
_viewport = Viewport::FromExclusive({ x, y, x + cx, y + cy });
|
||||
|
||||
_viewport = Viewport::FromExclusive(srCorrected);
|
||||
if (updateBottom)
|
||||
{
|
||||
UpdateBottom();
|
||||
|
||||
@@ -536,7 +536,12 @@ try
|
||||
outPipeTheirSide.reset();
|
||||
signalPipeTheirSide.reset();
|
||||
|
||||
const auto commandLine = fmt::format(FMT_COMPILE(L" --headless --signal {:#x}"), (int64_t)signalPipeOurSide.release());
|
||||
// GH#13211 - Make sure we request win32input mode and that the terminal
|
||||
// obeys the resizing quirk. Otherwise, defterm connections to the Terminal
|
||||
// are going to have weird resizing, and aren't going to send full fidelity
|
||||
// input messages.
|
||||
const auto commandLine = fmt::format(FMT_COMPILE(L" --headless --resizeQuirk --win32input --signal {:#x}"),
|
||||
(int64_t)signalPipeOurSide.release());
|
||||
|
||||
ConsoleArguments consoleArgs(commandLine, inPipeOurSide.release(), outPipeOurSide.release());
|
||||
RETURN_IF_FAILED(consoleArgs.ParseCommandline());
|
||||
|
||||
@@ -121,6 +121,7 @@ class ConptyOutputTests
|
||||
TEST_METHOD(WriteAFewSimpleLines);
|
||||
TEST_METHOD(InvalidateUntilOneBeforeEnd);
|
||||
TEST_METHOD(SetConsoleTitleWithControlChars);
|
||||
TEST_METHOD(IncludeBackgroundColorChangesInFirstFrame);
|
||||
|
||||
private:
|
||||
bool _writeCallback(const char* const pch, const size_t cch);
|
||||
@@ -371,6 +372,7 @@ void ConptyOutputTests::SetConsoleTitleWithControlChars()
|
||||
{
|
||||
BEGIN_TEST_METHOD_PROPERTIES()
|
||||
TEST_METHOD_PROPERTY(L"Data:control", L"{0x00, 0x0A, 0x1B, 0x80, 0x9B, 0x9C}")
|
||||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
|
||||
END_TEST_METHOD_PROPERTIES()
|
||||
|
||||
int control;
|
||||
@@ -400,3 +402,29 @@ void ConptyOutputTests::SetConsoleTitleWithControlChars()
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
void ConptyOutputTests::IncludeBackgroundColorChangesInFirstFrame()
|
||||
{
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
|
||||
sm.ProcessString(L"\x1b[41mRun 1 \x1b[42mRun 2 \x1b[43mRun 3 \x1b[m");
|
||||
|
||||
expectedOutput.push_back("\x1b[2J"); // standard init sequence for the first frame
|
||||
expectedOutput.push_back("\x1b[m"); // standard init sequence for the first frame
|
||||
expectedOutput.push_back("\x1b[41m");
|
||||
expectedOutput.push_back("\x1b[H"); // standard init sequence for the first frame
|
||||
expectedOutput.push_back("Run 1 ");
|
||||
expectedOutput.push_back("\x1b[42m");
|
||||
expectedOutput.push_back("Run 2 ");
|
||||
expectedOutput.push_back("\x1b[43m");
|
||||
expectedOutput.push_back("Run 3 ");
|
||||
|
||||
// This is also part of the standard init sequence.
|
||||
expectedOutput.push_back("\x1b[?25h");
|
||||
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
}
|
||||
|
||||
@@ -6291,6 +6291,18 @@ void ScreenBufferTests::UpdateVirtualBottomAfterResizeWithReflow()
|
||||
Log::Comment(L"Confirm that the virtual viewport includes the last non-space row");
|
||||
const auto lastNonSpaceRow = si.GetTextBuffer().GetLastNonSpaceCharacter().Y;
|
||||
VERIFY_IS_GREATER_THAN_OR_EQUAL(si._virtualBottom, lastNonSpaceRow);
|
||||
|
||||
Log::Comment(L"Clear the screen and note the cursor distance to the virtual bottom");
|
||||
stateMachine.ProcessString(L"\033[H\033[2J");
|
||||
const auto cursorDistanceFromBottom = si._virtualBottom - si.GetTextBuffer().GetCursor().GetPosition().Y;
|
||||
VERIFY_ARE_EQUAL(si.GetViewport().Height() - 1, cursorDistanceFromBottom);
|
||||
|
||||
Log::Comment(L"Stretch the viewport back to full width");
|
||||
bufferSize.X *= 2;
|
||||
VERIFY_NT_SUCCESS(si.ResizeWithReflow(bufferSize));
|
||||
|
||||
Log::Comment(L"Confirm cursor distance to the virtual bottom is unchanged");
|
||||
VERIFY_ARE_EQUAL(cursorDistanceFromBottom, si._virtualBottom - si.GetTextBuffer().GetCursor().GetPosition().Y);
|
||||
}
|
||||
|
||||
void ScreenBufferTests::DontShrinkVirtualBottomDuringResizeWithReflowAtTop()
|
||||
|
||||
@@ -13,21 +13,21 @@ namespace Microsoft::Console
|
||||
struct HostSignalNotifyAppData
|
||||
{
|
||||
uint32_t sizeInBytes;
|
||||
uint32_t processId;
|
||||
uint32_t processId; // THIS IS A PID
|
||||
};
|
||||
|
||||
struct HostSignalSetForegroundData
|
||||
{
|
||||
uint32_t sizeInBytes;
|
||||
uint32_t processId;
|
||||
uint32_t processId; // THIS IS A HANDLE, NOT A PID
|
||||
bool isForeground;
|
||||
};
|
||||
|
||||
struct HostSignalEndTaskData
|
||||
{
|
||||
uint32_t sizeInBytes;
|
||||
uint32_t processId;
|
||||
uint32_t processId; // THIS IS A PID
|
||||
uint32_t eventType;
|
||||
uint32_t ctrlFlags;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -168,15 +168,19 @@ public:
|
||||
auto actualAttrs = actual->TextAttr();
|
||||
auto expectedAttrs = expected->TextAttr();
|
||||
|
||||
auto mismatched = (actualChars != expectedChars || actualAttrs != expectedAttrs);
|
||||
auto mismatched = ((!expectedChars.empty() && actualChars != expectedChars) || actualAttrs != expectedAttrs);
|
||||
if (mismatched)
|
||||
{
|
||||
WEX::Logging::Log::Comment(WEX::Common::NoThrowString().Format(
|
||||
L"Character or attribute at index %d was mismatched", charsProcessed));
|
||||
}
|
||||
|
||||
VERIFY_ARE_EQUAL(expectedChars, actualChars);
|
||||
if (!expectedChars.empty())
|
||||
{
|
||||
VERIFY_ARE_EQUAL(expectedChars, actualChars);
|
||||
}
|
||||
VERIFY_ARE_EQUAL(expectedAttrs, actualAttrs);
|
||||
|
||||
if (mismatched)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -94,9 +94,11 @@ T HostSignalInputThread::_ReceiveTypedPacket()
|
||||
}
|
||||
case HostSignals::SetForeground:
|
||||
{
|
||||
auto msg = _ReceiveTypedPacket<HostSignalSetForegroundData>();
|
||||
_ReceiveTypedPacket<HostSignalSetForegroundData>();
|
||||
|
||||
LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->SetForeground(ULongToHandle(msg.processId), msg.isForeground));
|
||||
// GH#13211 - This honestly shouldn't be called by OpenConsole
|
||||
// anymore, but it's possible that a much older version of
|
||||
// OpenConsole is still calling this. Just do nothing.
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -104,7 +106,7 @@ T HostSignalInputThread::_ReceiveTypedPacket()
|
||||
{
|
||||
auto msg = _ReceiveTypedPacket<HostSignalEndTaskData>();
|
||||
|
||||
LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->EndTask(ULongToHandle(msg.processId), msg.eventType, msg.ctrlFlags));
|
||||
LOG_IF_NTSTATUS_FAILED(ServiceLocator::LocateConsoleControl()->EndTask(msg.processId, msg.eventType, msg.ctrlFlags));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -320,8 +320,14 @@ using namespace Microsoft::Console::Interactivity;
|
||||
// as far as the difference between parent/child and owner/owned
|
||||
// windows). Evan K said we should do it this way, and he
|
||||
// definitely knows.
|
||||
const auto windowStyle = WS_OVERLAPPEDWINDOW;
|
||||
const auto exStyles = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED;
|
||||
//
|
||||
// GH#13066: Load-bearing: Make sure to set WS_POPUP. If you
|
||||
// don't, then GetAncestor(GetConsoleWindow(), GA_ROOTOWNER)
|
||||
// will return the console handle again, not the owning
|
||||
// terminal's handle. It's not entirely clear why, but WS_POPUP
|
||||
// is absolutely vital for this to work correctly.
|
||||
const auto windowStyle = WS_OVERLAPPEDWINDOW | WS_POPUP;
|
||||
const auto exStyles = WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE;
|
||||
|
||||
// Attempt to create window.
|
||||
hwnd = CreateWindowExW(exStyles,
|
||||
@@ -335,7 +341,7 @@ using namespace Microsoft::Console::Interactivity;
|
||||
owner,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
this);
|
||||
|
||||
if (hwnd == nullptr)
|
||||
{
|
||||
@@ -430,6 +436,15 @@ void InteractivityFactory::SetPseudoWindowCallback(std::function<void(bool)> fun
|
||||
// It can be fun to toggle WM_QUERYOPEN but DefWindowProc returns TRUE.
|
||||
case WM_SIZE:
|
||||
{
|
||||
// Curiously, at least on Windows 10 (and rarely on Windows 11), if you
|
||||
// minimize the Terminal by clicking on the taskbar, then alt-tab to try
|
||||
// and restore the window, the Taskbar will decide to call
|
||||
// SwitchToWindow on us, the invisible, owned window of the main window.
|
||||
// When that happens, we'll get a WM_SIZE(SIZE_RESTORED, lParam=0). The
|
||||
// main window will NOT get a SwitchToWindow called. If we don't
|
||||
// actually inform the hosting process about this, then the main HWND
|
||||
// might stay hidden. Refer to GH#13589
|
||||
|
||||
if (wParam == SIZE_RESTORED)
|
||||
{
|
||||
_WritePseudoWindowCallback(true);
|
||||
@@ -441,23 +456,23 @@ void InteractivityFactory::SetPseudoWindowCallback(std::function<void(bool)> fun
|
||||
}
|
||||
break;
|
||||
}
|
||||
// case WM_WINDOWPOSCHANGING:
|
||||
// As long as user32 didn't eat the `ShowWindow` call because the window state requested
|
||||
// matches the existing WS_VISIBLE state of the HWND... we should hear from it in WM_WINDOWPOSCHANGING.
|
||||
// WM_WINDOWPOSCHANGING can tell us a bunch through the flags fields.
|
||||
// We can also check IsIconic/IsZoomed on the HWND during the message
|
||||
// and we could suppress the change to prevent things from happening.
|
||||
// case WM_WINDOWPOSCHANGING:
|
||||
// As long as user32 didn't eat the `ShowWindow` call because the window state requested
|
||||
// matches the existing WS_VISIBLE state of the HWND... we should hear from it in WM_WINDOWPOSCHANGING.
|
||||
// WM_WINDOWPOSCHANGING can tell us a bunch through the flags fields.
|
||||
// We can also check IsIconic/IsZoomed on the HWND during the message
|
||||
// and we could suppress the change to prevent things from happening.
|
||||
// case WM_SYSCOMMAND:
|
||||
// WM_SYSCOMMAND will not come through. Don't try.
|
||||
// WM_SYSCOMMAND will not come through. Don't try.
|
||||
// WM_SHOWWINDOW does come through on some of the messages.
|
||||
case WM_SHOWWINDOW:
|
||||
// WM_SHOWWINDOW comes through on some of the messages.
|
||||
{
|
||||
if (0 == lParam) // Someone explicitly called ShowWindow on us.
|
||||
{
|
||||
if (0 == lParam) // Someone explicitly called ShowWindow on us.
|
||||
{
|
||||
_WritePseudoWindowCallback((bool)wParam);
|
||||
}
|
||||
_WritePseudoWindowCallback((bool)wParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we get this far, call the default window proc
|
||||
return DefWindowProcW(hWnd, Message, wParam, lParam);
|
||||
}
|
||||
@@ -472,6 +487,11 @@ void InteractivityFactory::SetPseudoWindowCallback(std::function<void(bool)> fun
|
||||
// - <none>
|
||||
void InteractivityFactory::_WritePseudoWindowCallback(bool showOrHide)
|
||||
{
|
||||
// IMPORTANT!
|
||||
//
|
||||
// A hosting terminal window should only "restore" itself in response to
|
||||
// this message, if it's already minimized. If the window is maximized a
|
||||
// restore will restore-down the window instead.
|
||||
if (_pseudoWindowMessageCallback)
|
||||
{
|
||||
_pseudoWindowMessageCallback(showOrHide);
|
||||
|
||||
@@ -57,19 +57,17 @@ template<typename T>
|
||||
|
||||
[[nodiscard]] NTSTATUS RemoteConsoleControl::SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground)
|
||||
{
|
||||
HostSignalSetForegroundData data{};
|
||||
data.sizeInBytes = sizeof(data);
|
||||
data.processId = HandleToULong(hProcess);
|
||||
data.isForeground = fForeground;
|
||||
|
||||
return _SendTypedPacket(_pipe.get(), HostSignals::SetForeground, data);
|
||||
// GH#13211 - Apparently this API doesn't need to be forwarded to conhost at
|
||||
// all. Instead, just perform the ConsoleControl operation here, in proc.
|
||||
// This lets us avoid all sorts of strange handle duplicating weirdness.
|
||||
return _control.SetForeground(hProcess, fForeground);
|
||||
}
|
||||
|
||||
[[nodiscard]] NTSTATUS RemoteConsoleControl::EndTask(_In_ HANDLE hProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags)
|
||||
[[nodiscard]] NTSTATUS RemoteConsoleControl::EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags)
|
||||
{
|
||||
HostSignalEndTaskData data{};
|
||||
data.sizeInBytes = sizeof(data);
|
||||
data.processId = HandleToULong(hProcessId);
|
||||
data.processId = dwProcessId;
|
||||
data.eventType = dwEventType;
|
||||
data.ctrlFlags = ulCtrlFlags;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ Author(s):
|
||||
#pragma once
|
||||
|
||||
#include "../inc/IConsoleControl.hpp"
|
||||
#include "../win32/ConsoleControl.hpp"
|
||||
|
||||
namespace Microsoft::Console::Interactivity
|
||||
{
|
||||
@@ -25,9 +26,11 @@ namespace Microsoft::Console::Interactivity
|
||||
// IConsoleControl Members
|
||||
[[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId);
|
||||
[[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground);
|
||||
[[nodiscard]] NTSTATUS EndTask(_In_ HANDLE hProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags);
|
||||
[[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags);
|
||||
|
||||
private:
|
||||
wil::unique_handle _pipe;
|
||||
|
||||
Win32::ConsoleControl _control;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,6 @@ namespace Microsoft::Console::Interactivity
|
||||
virtual ~IConsoleControl() = default;
|
||||
[[nodiscard]] virtual NTSTATUS NotifyConsoleApplication(DWORD dwProcessId) = 0;
|
||||
[[nodiscard]] virtual NTSTATUS SetForeground(HANDLE hProcess, BOOL fForeground) = 0;
|
||||
[[nodiscard]] virtual NTSTATUS EndTask(HANDLE hProcessId, DWORD dwEventType, ULONG ulCtrlFlags) = 0;
|
||||
[[nodiscard]] virtual NTSTATUS EndTask(DWORD dwProcessId, DWORD dwEventType, ULONG ulCtrlFlags) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ using namespace Microsoft::Console::Interactivity::OneCore;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
[[nodiscard]] NTSTATUS ConsoleControl::EndTask(_In_ HANDLE hProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags)
|
||||
[[nodiscard]] NTSTATUS ConsoleControl::EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags)
|
||||
{
|
||||
USER_API_MSG m{};
|
||||
const auto a = &m.u.EndTask;
|
||||
|
||||
RtlZeroMemory(a, sizeof(*a));
|
||||
a->ProcessId = hProcessId;
|
||||
a->ProcessId = ULongToHandle(dwProcessId); // This is actually a PID, even though the struct expects a HANDLE.
|
||||
a->ConsoleEventCode = dwEventType;
|
||||
a->ConsoleFlags = ulCtrlFlags;
|
||||
|
||||
|
||||
@@ -26,6 +26,6 @@ namespace Microsoft::Console::Interactivity::OneCore
|
||||
// IConsoleControl Members
|
||||
[[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId) noexcept override;
|
||||
[[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground) noexcept override;
|
||||
[[nodiscard]] NTSTATUS EndTask(_In_ HANDLE hProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) override;
|
||||
[[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags) override;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -37,12 +37,12 @@ using namespace Microsoft::Console::Interactivity::Win32;
|
||||
sizeof(Flags));
|
||||
}
|
||||
|
||||
[[nodiscard]] NTSTATUS ConsoleControl::EndTask(_In_ HANDLE hProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags)
|
||||
[[nodiscard]] NTSTATUS ConsoleControl::EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags)
|
||||
{
|
||||
auto pConsoleWindow = ServiceLocator::LocateConsoleWindow();
|
||||
|
||||
CONSOLEENDTASK ConsoleEndTaskParams;
|
||||
ConsoleEndTaskParams.ProcessId = hProcessId;
|
||||
ConsoleEndTaskParams.ProcessId = ULongToHandle(dwProcessId); // This is actually a PID, even though the struct expects a HANDLE.
|
||||
ConsoleEndTaskParams.ConsoleEventCode = dwEventType;
|
||||
ConsoleEndTaskParams.ConsoleFlags = ulCtrlFlags;
|
||||
ConsoleEndTaskParams.hwnd = pConsoleWindow == nullptr ? nullptr : pConsoleWindow->GetWindowHandle();
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Microsoft::Console::Interactivity::Win32
|
||||
// IConsoleControl Members
|
||||
[[nodiscard]] NTSTATUS NotifyConsoleApplication(_In_ DWORD dwProcessId);
|
||||
[[nodiscard]] NTSTATUS SetForeground(_In_ HANDLE hProcess, _In_ BOOL fForeground);
|
||||
[[nodiscard]] NTSTATUS EndTask(_In_ HANDLE hProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags);
|
||||
[[nodiscard]] NTSTATUS EndTask(_In_ DWORD dwProcessId, _In_ DWORD dwEventType, _In_ ULONG ulCtrlFlags);
|
||||
|
||||
// Public Members
|
||||
[[nodiscard]] NTSTATUS Control(_In_ ConsoleControl::ControlType ConsoleCommand,
|
||||
|
||||
@@ -1036,9 +1036,18 @@ DWORD WINAPI ConsoleInputThreadProcWin32(LPVOID /*lpParameter*/)
|
||||
// If we are headless (because we're a pseudo console), we
|
||||
// will still need a window handle in the win32 environment
|
||||
// in case anyone sends messages at that HWND (vim.exe is an example.)
|
||||
// We have to CreateWindow on the same thread that will pump the messages
|
||||
// which is this thread.
|
||||
ServiceLocator::LocatePseudoWindow();
|
||||
//
|
||||
// IMPORTANT! We have to CreateWindow on the same thread that will pump
|
||||
// the messages, which is this thread. If you DON'T, then a DPI change
|
||||
// in the owning hwnd will cause us to get a dpi change as well, which
|
||||
// we'll never deque and handle, effectively HANGING THE OWNER HWND.
|
||||
// ServiceLocator::LocatePseudoWindow();
|
||||
//
|
||||
// Instead of just calling LocatePseudoWindow, make sure to go through
|
||||
// VtIo's CreatePseudoWindow, which will make sure that the window is
|
||||
// successfully created with the owner configured when the window is
|
||||
// first created. See GH#13066 for details.
|
||||
ServiceLocator::LocateGlobals().getConsoleInformation().GetVtIo()->CreatePseudoWindow();
|
||||
}
|
||||
|
||||
UnlockConsole();
|
||||
|
||||
@@ -174,9 +174,15 @@ constexpr HRESULT vec2_narrow(U x, U y, AtlasEngine::vec2<T>& out) noexcept
|
||||
|
||||
[[nodiscard]] HRESULT AtlasEngine::UpdateViewport(const SMALL_RECT srNewViewport) noexcept
|
||||
{
|
||||
_api.cellCount.x = gsl::narrow_cast<u16>(srNewViewport.Right - srNewViewport.Left + 1);
|
||||
_api.cellCount.y = gsl::narrow_cast<u16>(srNewViewport.Bottom - srNewViewport.Top + 1);
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
|
||||
const u16x2 cellCount{
|
||||
gsl::narrow_cast<u16>(srNewViewport.Right - srNewViewport.Left + 1),
|
||||
gsl::narrow_cast<u16>(srNewViewport.Bottom - srNewViewport.Top + 1),
|
||||
};
|
||||
if (_api.cellCount != cellCount)
|
||||
{
|
||||
_api.cellCount = cellCount;
|
||||
WI_SetFlag(_api.invalidations, ApiInvalidations::Size);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -433,10 +433,15 @@ using namespace Microsoft::Console::Types;
|
||||
// the inbox telnet client doesn't understand the Erase Character sequence,
|
||||
// and it uses xterm-ascii. This ensures that xterm and -256color consumers
|
||||
// get the enhancements, and telnet isn't broken.
|
||||
//
|
||||
// GH#13229: ECH and EL don't fill the space with "meta" attributes like
|
||||
// underline, reverse video, hyperlinks, etc. If these spaces had those
|
||||
// attrs, then don't try and optimize them out.
|
||||
const auto optimalToUseECH = numSpaces > ERASE_CHARACTER_STRING_LENGTH;
|
||||
const auto useEraseChar = (optimalToUseECH) &&
|
||||
(!_newBottomLine) &&
|
||||
(!_clearedAllThisFrame);
|
||||
(!_clearedAllThisFrame) &&
|
||||
(!_lastTextAttributes.HasAnyExtendedAttributes());
|
||||
const auto printingBottomLine = coord.Y == _lastViewport.BottomInclusive();
|
||||
|
||||
// GH#5502 - If the background color of the "new bottom line" is different
|
||||
@@ -457,12 +462,13 @@ using namespace Microsoft::Console::Types;
|
||||
// the lines _wrapped_. It doesn't care to manually break the lines, but if
|
||||
// we trimmed the spaces off here, we'd print all the "~"s one after another
|
||||
// on the same line.
|
||||
const auto removeSpaces = !lineWrapped && (useEraseChar ||
|
||||
_clearedAllThisFrame ||
|
||||
(_newBottomLine && printingBottomLine && bgMatched));
|
||||
const auto cchActual = removeSpaces ?
|
||||
(cchLine - numSpaces) :
|
||||
cchLine;
|
||||
static const TextAttribute defaultAttrs{};
|
||||
const bool removeSpaces = !lineWrapped && (useEraseChar // we determined earlier that ECH is optimal
|
||||
|| (_clearedAllThisFrame && _lastTextAttributes == defaultAttrs) // OR we cleared the last frame to the default attributes (specifically)
|
||||
|| (_newBottomLine && printingBottomLine && bgMatched)); // OR we just scrolled a new line onto the bottom of the screen with the correct attributes
|
||||
const size_t cchActual = removeSpaces ?
|
||||
(cchLine - numSpaces) :
|
||||
cchLine;
|
||||
|
||||
const auto columnsActual = removeSpaces ?
|
||||
(totalWidth - numSpaces) :
|
||||
|
||||
@@ -305,7 +305,7 @@ void ConsoleProcessList::ModifyConsoleProcessFocus(const bool fForeground)
|
||||
{
|
||||
const auto pProcessHandle = *it;
|
||||
|
||||
if (pProcessHandle->_hProcess != nullptr)
|
||||
if (pProcessHandle->_hProcess)
|
||||
{
|
||||
_ModifyProcessForegroundRights(pProcessHandle->_hProcess.get(), fForeground);
|
||||
}
|
||||
|
||||
@@ -150,15 +150,10 @@ bool InteractDispatch::MoveCursor(const VTInt row, const VTInt col)
|
||||
|
||||
const auto coordCursorShort = til::unwrap_coord(coordCursor);
|
||||
|
||||
// MSFT: 15813316 - Try to use this MoveCursor call to inherit the cursor position.
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(coordCursorShort));
|
||||
|
||||
// Finally, attempt to set the adjusted cursor position back into the console.
|
||||
auto& cursor = _api.GetTextBuffer().GetCursor();
|
||||
cursor.SetPosition(coordCursorShort);
|
||||
cursor.SetHasMoved(true);
|
||||
return true;
|
||||
const auto api = gsl::not_null{ ServiceLocator::LocateGlobals().api };
|
||||
auto& info = ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer();
|
||||
return SUCCEEDED(api->SetConsoleCursorPositionImpl(info, coordCursorShort));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@@ -204,11 +199,19 @@ bool InteractDispatch::FocusChanged(const bool focused) const
|
||||
{
|
||||
// They want focus, we found a pseudo hwnd.
|
||||
|
||||
// Note: ::GetParent(pseudoHwnd) will return 0. GetAncestor works though.
|
||||
// GA_PARENT and GA_ROOT seemingly return the same thing for
|
||||
// Terminal. We're going with GA_ROOT since it seems
|
||||
// semantically more correct here.
|
||||
if (const auto ownerHwnd{ ::GetAncestor(pseudoHwnd, GA_ROOT) })
|
||||
// BODGY
|
||||
//
|
||||
// This needs to be GA_ROOTOWNER here. Not GA_ROOT, GA_PARENT,
|
||||
// or GetParent. The ConPTY hwnd is an owned, top-level, popup,
|
||||
// non-parented window. It does not have a parent set. It does
|
||||
// have an owner set. It is not a WS_CHILD window. This
|
||||
// combination of things allows us to find the owning window
|
||||
// with GA_ROOTOWNER. GA_ROOT will get us ourselves, and
|
||||
// GA_PARENT will return the desktop HWND.
|
||||
//
|
||||
// See GH#13066
|
||||
|
||||
if (const auto ownerHwnd{ ::GetAncestor(pseudoHwnd, GA_ROOTOWNER) })
|
||||
{
|
||||
// We have an owner from a previous call to ReparentWindow
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
static void s_TerminalInputTestNullCallback(_In_ std::deque<std::unique_ptr<IInputEvent>>& inEvents);
|
||||
|
||||
TEST_METHOD(TerminalInputTests);
|
||||
TEST_METHOD(TestFocusEvents);
|
||||
TEST_METHOD(TerminalInputModifierKeyTests);
|
||||
TEST_METHOD(TerminalInputNullKeyTests);
|
||||
TEST_METHOD(DifferentModifiersTest);
|
||||
@@ -300,10 +301,62 @@ void InputTest::TerminalInputTests()
|
||||
inputEvent = IInputEvent::Create(irUnhandled);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify MENU_EVENT was NOT handled.");
|
||||
|
||||
Log::Comment(L"Testing FOCUS_EVENT");
|
||||
irUnhandled.EventType = FOCUS_EVENT;
|
||||
inputEvent = IInputEvent::Create(irUnhandled);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT was NOT handled.");
|
||||
// Testing FOCUS_EVENTs is handled by TestFocusEvents
|
||||
}
|
||||
|
||||
void InputTest::TestFocusEvents()
|
||||
{
|
||||
// GH#12900, #13238
|
||||
// Focus events that come in from the API should never be translated to VT sequences.
|
||||
// We're relying on the fact that the INPUT_RECORD version of the ctor is only called by the API
|
||||
const auto pInput = new TerminalInput(s_TerminalInputTestCallback);
|
||||
|
||||
INPUT_RECORD irTest = { 0 };
|
||||
irTest.EventType = FOCUS_EVENT;
|
||||
|
||||
{
|
||||
irTest.Event.FocusEvent.bSetFocus = false;
|
||||
auto inputEvent = IInputEvent::Create(irTest);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled.");
|
||||
}
|
||||
{
|
||||
irTest.Event.FocusEvent.bSetFocus = true;
|
||||
auto inputEvent = IInputEvent::Create(irTest);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled.");
|
||||
}
|
||||
{
|
||||
auto inputEvent = std::make_unique<FocusEvent>(false);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify FocusEvent from any other source was NOT handled.");
|
||||
}
|
||||
{
|
||||
auto inputEvent = std::make_unique<FocusEvent>(true);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify FocusEvent from any other source was NOT handled.");
|
||||
}
|
||||
|
||||
Log::Comment(L"Enable focus event handling");
|
||||
|
||||
pInput->SetInputMode(TerminalInput::Mode::FocusEvent, true);
|
||||
|
||||
{
|
||||
irTest.Event.FocusEvent.bSetFocus = false;
|
||||
auto inputEvent = IInputEvent::Create(irTest);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled.");
|
||||
}
|
||||
{
|
||||
irTest.Event.FocusEvent.bSetFocus = true;
|
||||
auto inputEvent = IInputEvent::Create(irTest);
|
||||
VERIFY_ARE_EQUAL(false, pInput->HandleKey(inputEvent.get()), L"Verify FOCUS_EVENT from API was NOT handled.");
|
||||
}
|
||||
{
|
||||
s_expectedInput = L"\x1b[O";
|
||||
auto inputEvent = std::make_unique<FocusEvent>(false);
|
||||
VERIFY_ARE_EQUAL(true, pInput->HandleKey(inputEvent.get()), L"Verify FocusEvent from any other source was handled.");
|
||||
}
|
||||
{
|
||||
s_expectedInput = L"\x1b[I";
|
||||
auto inputEvent = std::make_unique<FocusEvent>(true);
|
||||
VERIFY_ARE_EQUAL(true, pInput->HandleKey(inputEvent.get()), L"Verify FocusEvent from any other source was handled.");
|
||||
}
|
||||
}
|
||||
|
||||
void InputTest::TerminalInputModifierKeyTests()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user