Compare commits
124 commits
main
...
dev/migrie
Author | SHA1 | Date | |
---|---|---|---|
065e5f7d90 | |||
841ced7e57 | |||
056446cd3e | |||
25947c2f40 | |||
2445cedb92 | |||
8e43c9d8ce | |||
fd72b7992e | |||
bad27a97ba | |||
97d11d1bd3 | |||
7f03d4d1ea | |||
33e96e7e66 | |||
999f21fcf8 | |||
08cbd16d47 | |||
db9cbf3fa8 | |||
7024f44c96 | |||
1c66877b72 | |||
5253c114ae | |||
fdc574929b | |||
c09bdbd25e | |||
ce6a9c571b | |||
a93d17ef09 | |||
4f16dfb5fd | |||
b21287140d | |||
25b2675d8d | |||
fd849a5241 | |||
4976a091a0 | |||
c90eb8763a | |||
25c34dfcad | |||
9b4ae9ec55 | |||
3a8a83a810 | |||
bdf08165d4 | |||
b9979ffaf8 | |||
7fb490629b | |||
d6d708796a | |||
242de14722 | |||
69f1068050 | |||
90b79624ca | |||
fe5a78cff1 | |||
21d6ffe89c | |||
945c81d1df | |||
faa06f807d | |||
bf3c6e7029 | |||
f49c3fca01 | |||
ae99ce9c36 | |||
0e7217d354 | |||
aea37520b3 | |||
3c1866ac53 | |||
48b20de4f4 | |||
9ff2775122 | |||
b4e0496eff | |||
ff333870fc | |||
866832b665 | |||
56992296bf | |||
d053f6cc9e | |||
a751156fcc | |||
620ee302fd | |||
abb847b4c7 | |||
a3ac32ab25 | |||
5e9d0b8195 | |||
62fe8235dc | |||
5ff9a247d4 | |||
7e2b371dae | |||
02e9871f2a | |||
eee657b502 | |||
a6e044d91c | |||
f3738f5c1b | |||
7e2e4eaf49 | |||
8635537ebc | |||
de9dc32aa0 | |||
64d02f2b2b | |||
94c4cca176 | |||
b2796019f8 | |||
b755eb0f20 | |||
b1b1befeb9 | |||
507a48ed68 | |||
5a8e27e60a | |||
9b3b9e0109 | |||
7f9f75cab3 | |||
d0f05f60e9 | |||
56850639c5 | |||
a4acdeb5f2 | |||
56d5f9ee3a | |||
d944a68ded | |||
723037ef99 | |||
54a002762a | |||
47d55a8fd0 | |||
edd71265d8 | |||
6757452d6d | |||
4e69a32de7 | |||
da0cc7bae5 | |||
51e0473560 | |||
c106f64bc7 | |||
6be697221d | |||
d6c2fb593d | |||
1e3a319314 | |||
4da965f901 | |||
4f697eca92 | |||
1111d41347 | |||
6265f4f1d7 | |||
7854abe0a3 | |||
9a1cf5ac6e | |||
aef422d5f1 | |||
8569211d0f | |||
721b6367a2 | |||
5197dc4e50 | |||
880222dc1b | |||
447c2f9d4f | |||
b592155d2b | |||
64898b1caf | |||
e5dc64e085 | |||
58c6646132 | |||
9b32681ae1 | |||
7f29e1e268 | |||
736a351c27 | |||
631cdf7b18 | |||
7fb7d64b91 | |||
1ee3522cd8 | |||
ed1cf2aeac | |||
c66a56656e | |||
14d21f492b | |||
ccbcb425da | |||
2857324777 | |||
eb243f5e11 | |||
42c3eea136 |
|
@ -43,7 +43,8 @@
|
|||
<ClCompile Include="CommandlineTest.cpp" />
|
||||
<ClCompile Include="SettingsTests.cpp" />
|
||||
<ClCompile Include="TabTests.cpp" />
|
||||
<ClCompile Include="FilteredCommandTests.cpp" />
|
||||
<ClCompile Include="TrustCommandlineTests.cpp" />
|
||||
<ClCompile Include="FilteredCommandTests.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
|
|
123
src/cascadia/LocalTests_TerminalApp/TrustCommandlineTests.cpp
Normal file
123
src/cascadia/LocalTests_TerminalApp/TrustCommandlineTests.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// A series of tests for the TerminalPage::_isTrustedCommandline function.
|
||||
// That's a heuristic function for deciding if we should automatically trust
|
||||
// certain commandlines by default. The logic in there is weird and there are
|
||||
// lots of edge cases, so it's easier to just write a unit test.
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "../TerminalApp/TerminalPage.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace TerminalApp;
|
||||
using namespace winrt::TerminalApp;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
using namespace WEX::Logging;
|
||||
using namespace WEX::TestExecution;
|
||||
using namespace WEX::Common;
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::Text;
|
||||
|
||||
namespace winrt
|
||||
{
|
||||
namespace MUX = Microsoft::UI::Xaml;
|
||||
namespace WUX = Windows::UI::Xaml;
|
||||
using IInspectable = Windows::Foundation::IInspectable;
|
||||
}
|
||||
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class TrustCommandlineTests
|
||||
{
|
||||
BEGIN_TEST_CLASS(TrustCommandlineTests)
|
||||
END_TEST_CLASS()
|
||||
|
||||
TEST_METHOD(SimpleTests);
|
||||
TEST_METHOD(TestCommandlineWithArgs);
|
||||
TEST_METHOD(TestCommandlineWithSpaces);
|
||||
TEST_METHOD(TestCommandlineWithEnvVars);
|
||||
TEST_METHOD(WslTests);
|
||||
TEST_METHOD(TestPwshLocation);
|
||||
|
||||
bool trust(std::wstring_view cmdline);
|
||||
};
|
||||
|
||||
bool TrustCommandlineTests::trust(std::wstring_view cmdline)
|
||||
{
|
||||
return implementation::TerminalPage::_isTrustedCommandline(cmdline);
|
||||
}
|
||||
|
||||
void TrustCommandlineTests::SimpleTests()
|
||||
{
|
||||
VERIFY_IS_TRUE(trust(L"C:\\Windows\\System32\\cmd.exe"));
|
||||
VERIFY_IS_TRUE(trust(L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"));
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\i-definitely-don't-exist.exe"));
|
||||
|
||||
Log::Comment(L"These are not fully qualified, and _shouldn't_ be trusted");
|
||||
VERIFY_IS_FALSE(trust(L"cmd.exe"));
|
||||
VERIFY_IS_FALSE(trust(L"powershell.exe"));
|
||||
}
|
||||
|
||||
void TrustCommandlineTests::TestCommandlineWithArgs()
|
||||
{
|
||||
Log::Comment(L"These are sneaky things that _shouldn't_ be trusted");
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\cmd.exe /k echo Boo!"));
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\cmd.exe /k echo Boo! & cmd.exe"));
|
||||
}
|
||||
|
||||
void TrustCommandlineTests::TestCommandlineWithSpaces()
|
||||
{
|
||||
Log::Comment(L"This is a valid place for powershell to live, and the space can be tricky");
|
||||
VERIFY_IS_TRUE(trust(L"C:\\Program Files\\PowerShell\\7\\pwsh.exe"));
|
||||
|
||||
Log::Comment(L"These are sneaky things that _shouldn't_ be trusted");
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System 32\\cmd.exe"));
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\ cmd.exe"));
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\cmd.exe /c cmd.exe"));
|
||||
}
|
||||
|
||||
void TrustCommandlineTests::TestCommandlineWithEnvVars()
|
||||
{
|
||||
Log::Comment(L"Make sure we auto-expand environment variables");
|
||||
|
||||
VERIFY_IS_TRUE(trust(L"%WINDIR%\\system32\\cmd.exe"));
|
||||
VERIFY_IS_TRUE(trust(L"%WINDIR%\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"));
|
||||
VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe"));
|
||||
}
|
||||
|
||||
void TrustCommandlineTests::WslTests()
|
||||
{
|
||||
Log::Comment(L"We are explicitly deciding to not auto-approve "
|
||||
L"`wsl.exe -d distro`-like commandlines. If we change this"
|
||||
L" policy, remove this test.");
|
||||
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\wsl"));
|
||||
VERIFY_IS_TRUE(trust(L"C:\\Windows\\System32\\wsl.exe"), L"This we will trust though, since it's an exe in system32");
|
||||
VERIFY_IS_FALSE(trust(L"C:\\Windows\\System32\\wsl.exe -d Ubuntu"));
|
||||
VERIFY_IS_FALSE(trust(L"wsl.exe"));
|
||||
}
|
||||
|
||||
void TrustCommandlineTests::TestPwshLocation()
|
||||
{
|
||||
Log::Comment(L"Test various locations that pwsh.exe can be in");
|
||||
|
||||
VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe"));
|
||||
VERIFY_IS_TRUE(trust(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps\\pwsh.exe"));
|
||||
VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\10\\pwsh.exe"));
|
||||
VERIFY_IS_TRUE(trust(L"%ProgramFiles%\\PowerShell\\7.1.5\\pwsh.exe"));
|
||||
|
||||
Log::Comment(L"These are sneaky things that _shouldn't_ be trusted");
|
||||
VERIFY_IS_FALSE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe bad-stuff pwsh.exe"));
|
||||
VERIFY_IS_FALSE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe bad-stuff c:\\pwsh.exe"));
|
||||
VERIFY_IS_FALSE(trust(L"%ProgramFiles%\\PowerShell\\7\\pwsh.exe bad-stuff c:\\ %ProgramFiles%\\PowerShell\\7\\pwsh.exe"));
|
||||
}
|
||||
}
|
74
src/cascadia/TerminalApp/AdminWarningPlaceholder.cpp
Normal file
74
src/cascadia/TerminalApp/AdminWarningPlaceholder.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "AdminWarningPlaceholder.h"
|
||||
#include "AdminWarningPlaceholder.g.cpp"
|
||||
#include <LibraryResources.h>
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
AdminWarningPlaceholder::AdminWarningPlaceholder(const winrt::Microsoft::Terminal::Control::TermControl& control, const winrt::hstring& cmdline) :
|
||||
_control{ control },
|
||||
_Commandline{ cmdline }
|
||||
{
|
||||
InitializeComponent();
|
||||
// If the content we're hosting is a TermControl, then use the control's
|
||||
// BG as our BG, to give the impression that it's there, under the
|
||||
// dialog.
|
||||
if (const auto termControl{ control.try_as<winrt::Microsoft::Terminal::Control::TermControl>() })
|
||||
{
|
||||
RootGrid().Background(termControl.BackgroundBrush());
|
||||
}
|
||||
}
|
||||
void AdminWarningPlaceholder::_primaryButtonClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
||||
RoutedEventArgs const& e)
|
||||
{
|
||||
_PrimaryButtonClickedHandlers(*this, e);
|
||||
}
|
||||
void AdminWarningPlaceholder::_cancelButtonClick(winrt::Windows::Foundation::IInspectable const& /*sender*/,
|
||||
RoutedEventArgs const& e)
|
||||
{
|
||||
_CancelButtonClickedHandlers(*this, e);
|
||||
}
|
||||
winrt::Windows::UI::Xaml::Controls::UserControl AdminWarningPlaceholder::Control()
|
||||
{
|
||||
return _control;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Move the focus to the cancel button by default. This has the LOAD
|
||||
// BEARING side effect of also triggering Narrator to read out the
|
||||
// contents of the dialog. It's unclear why doing this works, but it does.
|
||||
// - Using a LayoutUpdated event to trigger the focus change when we're
|
||||
// added to the UI tree did not seem to work.
|
||||
// - Whoever is adding us to the UI tree is responsible for calling this!
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void AdminWarningPlaceholder::FocusOnLaunch()
|
||||
{
|
||||
CancelButton().Focus(FocusState::Programmatic);
|
||||
}
|
||||
|
||||
winrt::hstring AdminWarningPlaceholder::ControlName()
|
||||
{
|
||||
return RS_(L"AdminWarningPlaceholderName");
|
||||
}
|
||||
|
||||
void AdminWarningPlaceholder::_keyUpHandler(IInspectable const& /*sender*/,
|
||||
Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e)
|
||||
{
|
||||
// If the user presses escape, close the dialog (without confirming)
|
||||
const auto key = e.OriginalKey();
|
||||
if (key == winrt::Windows::System::VirtualKey::Escape)
|
||||
{
|
||||
_CancelButtonClickedHandlers(*this, e);
|
||||
e.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
51
src/cascadia/TerminalApp/AdminWarningPlaceholder.h
Normal file
51
src/cascadia/TerminalApp/AdminWarningPlaceholder.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Module Name:
|
||||
- AdminWarningPlaceholder
|
||||
|
||||
Abstract:
|
||||
- The AdminWarningPlaceholder is a control used to fill space in a pane and
|
||||
present a warning to the user. It holds on to a real control that it should be
|
||||
replaced with. It looks just like a ContentDialog, except it exists per-pane,
|
||||
whereas a ContentDialog can only be added to take up the whole window.
|
||||
- The caller should make sure to bind our PrimaryButtonClicked and
|
||||
CancelButtonClicked events, to be informed as to which was pressed.
|
||||
|
||||
Author(s):
|
||||
- Mike Griese - September 2021
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AdminWarningPlaceholder.g.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct AdminWarningPlaceholder : AdminWarningPlaceholderT<AdminWarningPlaceholder>
|
||||
{
|
||||
AdminWarningPlaceholder(const winrt::Microsoft::Terminal::Control::TermControl& control, const winrt::hstring& cmdline);
|
||||
void FocusOnLaunch();
|
||||
winrt::Windows::UI::Xaml::Controls::UserControl Control();
|
||||
winrt::hstring ControlName();
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, Commandline, _PropertyChangedHandlers);
|
||||
TYPED_EVENT(PrimaryButtonClicked, TerminalApp::AdminWarningPlaceholder, winrt::Windows::UI::Xaml::RoutedEventArgs);
|
||||
TYPED_EVENT(CancelButtonClicked, TerminalApp::AdminWarningPlaceholder, winrt::Windows::UI::Xaml::RoutedEventArgs);
|
||||
|
||||
private:
|
||||
friend struct AdminWarningPlaceholderT<AdminWarningPlaceholder>; // friend our parent so it can bind private event handlers
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
|
||||
|
||||
void _primaryButtonClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void _cancelButtonClick(winrt::Windows::Foundation::IInspectable const& sender,
|
||||
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
|
||||
void _keyUpHandler(Windows::Foundation::IInspectable const& sender,
|
||||
Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
|
||||
};
|
||||
}
|
12
src/cascadia/TerminalApp/AdminWarningPlaceholder.idl
Normal file
12
src/cascadia/TerminalApp/AdminWarningPlaceholder.idl
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass AdminWarningPlaceholder : Windows.UI.Xaml.Controls.UserControl,
|
||||
Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
String Commandline { get; };
|
||||
String ControlName { get; };
|
||||
}
|
||||
}
|
97
src/cascadia/TerminalApp/AdminWarningPlaceholder.xaml
Normal file
97
src/cascadia/TerminalApp/AdminWarningPlaceholder.xaml
Normal file
|
@ -0,0 +1,97 @@
|
|||
<!--
|
||||
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
|
||||
the MIT License. See LICENSE in the project root for license information.
|
||||
-->
|
||||
<UserControl x:Class="TerminalApp.AdminWarningPlaceholder"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:TerminalApp"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
|
||||
AutomationProperties.AccessibilityView="Content"
|
||||
AutomationProperties.IsDialog="True"
|
||||
AutomationProperties.Name="{x:Bind ControlName, Mode=OneWay}"
|
||||
PreviewKeyUp="_keyUpHandler"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!--
|
||||
We have to use two grids to be tricky here:
|
||||
- The outer grid will get its background from the control it hosts, and
|
||||
expands to fit its container. This will make it look like the background
|
||||
fills the space available.
|
||||
- The inner grid is set to Center,Center, so that the "dialog" appears
|
||||
centered
|
||||
-->
|
||||
|
||||
<Grid x:Name="RootGrid"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<Border Margin="8,8,8,8"
|
||||
Padding="16,8,16,8"
|
||||
AllowFocusOnInteraction="True"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
AutomationProperties.IsDialog="True"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
BorderBrush="{ThemeResource SystemAccentColor}"
|
||||
BorderThickness="2,2,2,2"
|
||||
CornerRadius="{ThemeResource OverlayCornerRadius}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock x:Name="ApproveCommandlineWarningTitle"
|
||||
x:Uid="ApproveCommandlineWarningTitle"
|
||||
Padding="0,0,0,16"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="20"
|
||||
FontWeight="Normal"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
|
||||
<TextBlock x:Name="PrefixTextBlock"
|
||||
x:Uid="ApproveCommandlineWarningPrefixTextBlock"
|
||||
HorizontalAlignment="Left"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
|
||||
<TextBlock x:Name="CommandlineText"
|
||||
Margin="0,16,0,16"
|
||||
HorizontalAlignment="Left"
|
||||
FontFamily="Cascadia Mono"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind Commandline, Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
|
||||
<TextBlock x:Name="SuffixTextBlock"
|
||||
x:Uid="ApproveCommandlineWarningSuffixTextBlock"
|
||||
HorizontalAlignment="Left"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<Grid Margin="0,8,0,8"
|
||||
HorizontalAlignment="Stretch"
|
||||
XYFocusKeyboardNavigation="Enabled">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button x:Name="PrimaryButton"
|
||||
x:Uid="ApproveCommandlineWarning_PrimaryButton"
|
||||
Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="_primaryButtonClick"
|
||||
IsTabStop="True"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<Button x:Name="CancelButton"
|
||||
x:Uid="ApproveCommandlineWarning_CancelButton"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Click="_cancelButtonClick"
|
||||
IsTabStop="True" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
|
@ -34,7 +34,7 @@ static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Wi
|
|||
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr };
|
||||
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr };
|
||||
|
||||
Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) :
|
||||
Pane::Pane(const Profile& profile, const Controls::UserControl& control, const bool lastFocused) :
|
||||
_control{ control },
|
||||
_lastActive{ lastFocused },
|
||||
_profile{ profile }
|
||||
|
@ -42,8 +42,11 @@ Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFo
|
|||
_root.Children().Append(_borderFirst);
|
||||
_borderFirst.Child(_control);
|
||||
|
||||
_connectionStateChangedToken = _control.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
|
||||
_warningBellToken = _control.WarningBell({ this, &Pane::_ControlWarningBellHandler });
|
||||
if (const auto& termControl{ _control.try_as<TermControl>() })
|
||||
{
|
||||
_connectionStateChangedToken = termControl.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
|
||||
_warningBellToken = termControl.WarningBell({ this, &Pane::_ControlWarningBellHandler });
|
||||
}
|
||||
|
||||
// On the first Pane's creation, lookup resources we'll use to theme the
|
||||
// Pane, including the brushed to use for the focused/unfocused border
|
||||
|
@ -125,11 +128,23 @@ NewTerminalArgs Pane::GetTerminalArgsForPane() const
|
|||
assert(_IsLeaf());
|
||||
|
||||
NewTerminalArgs args{};
|
||||
auto controlSettings = _control.Settings().as<TerminalSettings>();
|
||||
auto termControl{ _control.try_as<TermControl>() };
|
||||
if (!termControl)
|
||||
{
|
||||
if (auto adminWarning{ _control.try_as<AdminWarningPlaceholder>() })
|
||||
{
|
||||
termControl = adminWarning.Content().try_as<TermControl>();
|
||||
}
|
||||
}
|
||||
if (!termControl)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
auto controlSettings = termControl.Settings().as<TerminalSettings>();
|
||||
|
||||
args.Profile(controlSettings.ProfileName());
|
||||
// If we know the user's working directory use it instead of the profile.
|
||||
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
|
||||
if (const auto dir = termControl.WorkingDirectory(); !dir.empty())
|
||||
{
|
||||
args.StartingDirectory(dir);
|
||||
}
|
||||
|
@ -832,6 +847,40 @@ bool Pane::SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second)
|
|||
return false;
|
||||
}
|
||||
|
||||
Controls::UserControl Pane::ReplaceControl(const Controls::UserControl& control)
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Remove old control's event handlers
|
||||
const auto& oldControl = _control;
|
||||
_gotFocusRevoker.revoke();
|
||||
_lostFocusRevoker.revoke();
|
||||
if (const auto& oldTermControl{ _control.try_as<TermControl>() })
|
||||
{
|
||||
oldTermControl.ConnectionStateChanged(_connectionStateChangedToken);
|
||||
oldTermControl.WarningBell(_warningBellToken);
|
||||
}
|
||||
|
||||
_control = control;
|
||||
|
||||
_borderFirst.Child(_control);
|
||||
|
||||
// Register an event with the control to have it inform us when it gains focus.
|
||||
_gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
|
||||
_lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler });
|
||||
|
||||
if (const auto& termControl{ _control.try_as<TermControl>() })
|
||||
{
|
||||
_connectionStateChangedToken = termControl.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
|
||||
_warningBellToken = termControl.WarningBell({ this, &Pane::_ControlWarningBellHandler });
|
||||
}
|
||||
|
||||
return oldControl;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Given two panes' offsets, test whether the `direction` side of first is adjacent to second.
|
||||
// Arguments:
|
||||
|
@ -1079,8 +1128,12 @@ void Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundatio
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto newConnectionState = _control.ConnectionState();
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
if (!termControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto newConnectionState = termControl.ConnectionState();
|
||||
const auto previousConnectionState = std::exchange(_connectionState, newConnectionState);
|
||||
|
||||
if (newConnectionState < ConnectionState::Closed)
|
||||
|
@ -1123,7 +1176,9 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect
|
|||
{
|
||||
return;
|
||||
}
|
||||
if (_profile)
|
||||
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
if (_profile && termControl)
|
||||
{
|
||||
// We don't want to do anything if nothing is set, so check for that first
|
||||
if (static_cast<int>(_profile.BellStyle()) != 0)
|
||||
|
@ -1137,7 +1192,7 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect
|
|||
|
||||
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
|
||||
{
|
||||
_control.BellLightOn();
|
||||
termControl.BellLightOn();
|
||||
}
|
||||
|
||||
// raise the event with the bool value corresponding to the taskbar flag
|
||||
|
@ -1197,7 +1252,11 @@ void Pane::Shutdown()
|
|||
std::unique_lock lock{ _createCloseLock };
|
||||
if (_IsLeaf())
|
||||
{
|
||||
_control.Close();
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
if (termControl)
|
||||
{
|
||||
termControl.Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1207,7 +1266,7 @@ void Pane::Shutdown()
|
|||
}
|
||||
|
||||
// Method Description:
|
||||
// - Get the root UIElement of this pane. There may be a single TermControl as a
|
||||
// - Get the root UIElement of this pane. There may be a single UserControl as a
|
||||
// child, or an entire tree of grids and panes as children of this element.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
|
@ -1266,7 +1325,7 @@ TermControl Pane::GetLastFocusedTerminalControl()
|
|||
{
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
return p->_control;
|
||||
return p->GetTerminalControl();
|
||||
}
|
||||
pane = p;
|
||||
}
|
||||
|
@ -1274,7 +1333,7 @@ TermControl Pane::GetLastFocusedTerminalControl()
|
|||
}
|
||||
return _firstChild->GetLastFocusedTerminalControl();
|
||||
}
|
||||
return _control;
|
||||
return GetTerminalControl();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1283,8 +1342,15 @@ TermControl Pane::GetLastFocusedTerminalControl()
|
|||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - nullptr if this Pane is a parent, otherwise the TermControl of this Pane.
|
||||
TermControl Pane::GetTerminalControl()
|
||||
// - nullptr if this Pane is a parent or isn't hosting a Terminal, otherwise the
|
||||
// TermControl of this Pane.
|
||||
TermControl Pane::GetTerminalControl() const
|
||||
{
|
||||
auto control{ GetUserControl() };
|
||||
return control ? control.try_as<TermControl>() : nullptr;
|
||||
}
|
||||
|
||||
Controls::UserControl Pane::GetUserControl() const
|
||||
{
|
||||
return _IsLeaf() ? _control : nullptr;
|
||||
}
|
||||
|
@ -1457,9 +1523,13 @@ void Pane::_FocusFirstChild()
|
|||
void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile)
|
||||
{
|
||||
assert(_IsLeaf());
|
||||
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
if (!termControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_profile = profile;
|
||||
auto controlSettings = _control.Settings().as<TerminalSettings>();
|
||||
auto controlSettings = termControl.Settings().as<TerminalSettings>();
|
||||
// Update the parent of the control's settings object (and not the object itself) so
|
||||
// that any overrides made by the control don't get affected by the reload
|
||||
controlSettings.SetParent(settings.DefaultSettings());
|
||||
|
@ -1472,8 +1542,8 @@ void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Pr
|
|||
// sure the unfocused settings inherit from that.
|
||||
unfocusedSettings.SetParent(controlSettings);
|
||||
}
|
||||
_control.UnfocusedAppearance(unfocusedSettings);
|
||||
_control.UpdateSettings();
|
||||
termControl.UnfocusedAppearance(unfocusedSettings);
|
||||
termControl.UpdateSettings();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1614,8 +1684,12 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
|||
_id = remainingChild->Id();
|
||||
|
||||
// Add our new event handler before revoking the old one.
|
||||
_connectionStateChangedToken = _control.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
|
||||
_warningBellToken = _control.WarningBell({ this, &Pane::_ControlWarningBellHandler });
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
if (termControl)
|
||||
{
|
||||
_connectionStateChangedToken = termControl.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
|
||||
_warningBellToken = termControl.WarningBell({ this, &Pane::_ControlWarningBellHandler });
|
||||
}
|
||||
|
||||
// Revoke the old event handlers. Remove both the handlers for the panes
|
||||
// themselves closing, and remove their handlers for their controls
|
||||
|
@ -1629,8 +1703,11 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
|||
closedChild->WalkTree([](auto p) {
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->_control.ConnectionStateChanged(p->_connectionStateChangedToken);
|
||||
p->_control.WarningBell(p->_warningBellToken);
|
||||
if (const auto& closedControl{ p->_control.try_as<TermControl>() })
|
||||
{
|
||||
closedControl.ConnectionStateChanged(p->_connectionStateChangedToken);
|
||||
closedControl.WarningBell(p->_warningBellToken);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
@ -1638,15 +1715,19 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
|||
|
||||
closedChild->Closed(closedChildClosedToken);
|
||||
remainingChild->Closed(remainingChildClosedToken);
|
||||
remainingChild->_control.ConnectionStateChanged(remainingChild->_connectionStateChangedToken);
|
||||
remainingChild->_control.WarningBell(remainingChild->_warningBellToken);
|
||||
|
||||
if (const auto& remainingControl{ remainingChild->_control.try_as<TermControl>() })
|
||||
{
|
||||
remainingControl.ConnectionStateChanged(remainingChild->_connectionStateChangedToken);
|
||||
remainingControl.WarningBell(remainingChild->_warningBellToken);
|
||||
}
|
||||
|
||||
// If we or either of our children was focused, we want to take that
|
||||
// focus from them.
|
||||
_lastActive = _lastActive || _firstChild->_lastActive || _secondChild->_lastActive;
|
||||
|
||||
// Remove all the ui elements of the remaining child. This'll make sure
|
||||
// we can re-attach the TermControl to our Grid.
|
||||
// we can re-attach the UserControl to our Grid.
|
||||
remainingChild->_root.Children().Clear();
|
||||
remainingChild->_borderFirst.Child(nullptr);
|
||||
|
||||
|
@ -1657,7 +1738,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
|||
_root.ColumnDefinitions().Clear();
|
||||
_root.RowDefinitions().Clear();
|
||||
|
||||
// Reattach the TermControl to our grid.
|
||||
// Reattach the UserControl to our grid.
|
||||
_root.Children().Append(_borderFirst);
|
||||
_borderFirst.Child(_control);
|
||||
|
||||
|
@ -1719,8 +1800,11 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
|||
closedChild->WalkTree([](auto p) {
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->_control.ConnectionStateChanged(p->_connectionStateChangedToken);
|
||||
p->_control.WarningBell(p->_warningBellToken);
|
||||
if (const auto& closedControl{ p->_control.try_as<TermControl>() })
|
||||
{
|
||||
closedControl.ConnectionStateChanged(p->_connectionStateChangedToken);
|
||||
closedControl.WarningBell(p->_warningBellToken);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
@ -2468,11 +2552,14 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
|
|||
|
||||
if (_IsLeaf())
|
||||
{
|
||||
// revoke our handler - the child will take care of the control now.
|
||||
_control.ConnectionStateChanged(_connectionStateChangedToken);
|
||||
_connectionStateChangedToken.value = 0;
|
||||
_control.WarningBell(_warningBellToken);
|
||||
_warningBellToken.value = 0;
|
||||
if (const auto& termControl{ _control.try_as<TermControl>() })
|
||||
{
|
||||
// revoke our handler - the child will take care of the control now.
|
||||
termControl.ConnectionStateChanged(_connectionStateChangedToken);
|
||||
termControl.WarningBell(_warningBellToken);
|
||||
_connectionStateChangedToken.value = 0;
|
||||
_warningBellToken.value = 0;
|
||||
}
|
||||
|
||||
// Remove our old GotFocus handler from the control. We don't want the
|
||||
// control telling us that it's now focused, we want it telling its new
|
||||
|
@ -2482,7 +2569,7 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
|
|||
}
|
||||
|
||||
// Remove any children we currently have. We can't add the existing
|
||||
// TermControl to a new grid until we do this.
|
||||
// UserControl to a new grid until we do this.
|
||||
_root.Children().Clear();
|
||||
_borderFirst.Child(nullptr);
|
||||
_borderSecond.Child(nullptr);
|
||||
|
@ -2860,8 +2947,13 @@ float Pane::CalcSnappedDimension(const bool widthOrHeight, const float dimension
|
|||
// If requested size is already snapped, then both returned values equal this value.
|
||||
Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
|
||||
{
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
if (_IsLeaf())
|
||||
{
|
||||
if (!termControl)
|
||||
{
|
||||
return { dimension, dimension };
|
||||
}
|
||||
// If we're a leaf pane, align to the grid of controlling terminal
|
||||
|
||||
const auto minSize = _GetMinSize();
|
||||
|
@ -2872,7 +2964,7 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
|
|||
return { minDimension, minDimension };
|
||||
}
|
||||
|
||||
float lower = _control.SnapDimensionToGrid(widthOrHeight, dimension);
|
||||
float lower = termControl.SnapDimensionToGrid(widthOrHeight, dimension);
|
||||
if (widthOrHeight)
|
||||
{
|
||||
lower += WI_IsFlagSet(_borders, Borders::Left) ? PaneBorderSize : 0;
|
||||
|
@ -2892,7 +2984,7 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
|
|||
}
|
||||
else
|
||||
{
|
||||
const auto cellSize = _control.CharacterDimensions();
|
||||
const auto cellSize = termControl.CharacterDimensions();
|
||||
const auto higher = lower + (widthOrHeight ? cellSize.Width : cellSize.Height);
|
||||
return { lower, higher };
|
||||
}
|
||||
|
@ -2937,26 +3029,39 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
|
|||
// - <none>
|
||||
void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& sizeNode) const
|
||||
{
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
if (_IsLeaf())
|
||||
{
|
||||
// We're a leaf pane, so just add one more row or column (unless isMinimumSize
|
||||
// is true, see below).
|
||||
|
||||
if (sizeNode.isMinimumSize)
|
||||
if (termControl)
|
||||
{
|
||||
// If the node is of its minimum size, this size might not be snapped (it might
|
||||
// be, say, half a character, or fixed 10 pixels), so snap it upward. It might
|
||||
// however be already snapped, so add 1 to make sure it really increases
|
||||
// (not strictly necessary but to avoid surprises).
|
||||
sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher;
|
||||
// We're a leaf pane, so just add one more row or column (unless isMinimumSize
|
||||
// is true, see below).
|
||||
|
||||
if (sizeNode.isMinimumSize)
|
||||
{
|
||||
// If the node is of its minimum size, this size might not be snapped (it might
|
||||
// be, say, half a character, or fixed 10 pixels), so snap it upward. It might
|
||||
// however be already snapped, so add 1 to make sure it really increases
|
||||
// (not strictly necessary but to avoid surprises).
|
||||
sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cellSize = termControl.CharacterDimensions();
|
||||
sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cellSize = _control.CharacterDimensions();
|
||||
sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height;
|
||||
// If we're a leaf that didn't have a TermControl, then just increment
|
||||
// by one. We have to increment by _some_ value, because this is used in
|
||||
// a while() loop to find the next bigger size we can snap to. But since
|
||||
// a non-terminal control doesn't really care what size it's snapped to,
|
||||
// we can just say "one pixel larger is the next snap point"
|
||||
sizeNode.size += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
else // !_IsLeaf()
|
||||
{
|
||||
// We're a parent pane, so we have to advance dimension of our children panes. In
|
||||
// fact, we advance only one child (chosen later) to keep the growth fine-grained.
|
||||
|
@ -3058,7 +3163,8 @@ Size Pane::_GetMinSize() const
|
|||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
auto controlSize = _control.MinimumSize();
|
||||
const auto& termControl{ _control.try_as<TermControl>() };
|
||||
auto controlSize = termControl ? termControl.MinimumSize() : Size{ 1, 1 };
|
||||
auto newWidth = controlSize.Width;
|
||||
auto newHeight = controlSize.Height;
|
||||
|
||||
|
@ -3244,7 +3350,10 @@ std::optional<SplitDirection> Pane::PreCalculateAutoSplit(const std::shared_ptr<
|
|||
// - Returns true if the pane or one of its descendants is read-only
|
||||
bool Pane::ContainsReadOnly() const
|
||||
{
|
||||
return _IsLeaf() ? _control.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly());
|
||||
const auto& termControl{ GetTerminalControl() };
|
||||
return termControl ?
|
||||
termControl.ReadOnly() :
|
||||
(_IsLeaf() ? false : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly()));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -3257,13 +3366,14 @@ bool Pane::ContainsReadOnly() const
|
|||
// - <none>
|
||||
void Pane::CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& states)
|
||||
{
|
||||
if (_IsLeaf())
|
||||
const auto& termControl{ GetTerminalControl() };
|
||||
if (termControl)
|
||||
{
|
||||
auto tbState{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>(_control.TaskbarState(),
|
||||
_control.TaskbarProgress()) };
|
||||
auto tbState{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>(termControl.TaskbarState(),
|
||||
termControl.TaskbarProgress()) };
|
||||
states.push_back(tbState);
|
||||
}
|
||||
else
|
||||
else if (!_IsLeaf())
|
||||
{
|
||||
_firstChild->CollectTaskbarStates(states);
|
||||
_secondChild->CollectTaskbarStates(states);
|
||||
|
|
|
@ -56,7 +56,7 @@ class Pane : public std::enable_shared_from_this<Pane>
|
|||
{
|
||||
public:
|
||||
Pane(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control,
|
||||
const winrt::Windows::UI::Xaml::Controls::UserControl& control,
|
||||
const bool lastFocused = false);
|
||||
|
||||
Pane(std::shared_ptr<Pane> first,
|
||||
|
@ -66,8 +66,9 @@ public:
|
|||
const bool lastFocused = false);
|
||||
|
||||
std::shared_ptr<Pane> GetActivePane();
|
||||
winrt::Windows::UI::Xaml::Controls::UserControl GetUserControl() const;
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl();
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl() const;
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
|
||||
|
||||
// Method Description:
|
||||
|
@ -126,6 +127,8 @@ public:
|
|||
winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType);
|
||||
std::shared_ptr<Pane> DetachPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::UserControl ReplaceControl(const winrt::Windows::UI::Xaml::Controls::UserControl& control);
|
||||
|
||||
int GetLeafPaneCount() const noexcept;
|
||||
|
||||
void Maximize(std::shared_ptr<Pane> zoomedPane);
|
||||
|
@ -200,7 +203,8 @@ private:
|
|||
winrt::Windows::UI::Xaml::Controls::Grid _root{};
|
||||
winrt::Windows::UI::Xaml::Controls::Border _borderFirst{};
|
||||
winrt::Windows::UI::Xaml::Controls::Border _borderSecond{};
|
||||
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::UserControl _control{ nullptr };
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
|
||||
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
|
||||
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush;
|
||||
|
|
|
@ -504,6 +504,24 @@
|
|||
<data name="MultiLinePasteDialog.Title" xml:space="preserve">
|
||||
<value>Warning</value>
|
||||
</data>
|
||||
<data name="ApproveCommandlineWarningPrefixTextBlock.Text" xml:space="preserve">
|
||||
<value>You are about to execute the following command-line:</value>
|
||||
</data>
|
||||
<data name="ApproveCommandlineWarningSuffixTextBlock.Text" xml:space="preserve">
|
||||
<value>Do you wish to continue?</value>
|
||||
</data>
|
||||
<data name="ApproveCommandlineWarning_PrimaryButton.Content" xml:space="preserve">
|
||||
<value>Allow command-line</value>
|
||||
</data>
|
||||
<data name="ApproveCommandlineWarning_CancelButton.Content" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="ApproveCommandlineWarningTitle.Text" xml:space="preserve">
|
||||
<value>Warning</value>
|
||||
</data>
|
||||
<data name="AdminWarningPlaceholderName" xml:space="preserve">
|
||||
<value>Elevated command-line warning</value>
|
||||
</data>
|
||||
<data name="CommandPalette_SearchBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Type a command name...</value>
|
||||
</data>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <LibraryResources.h>
|
||||
|
||||
#include "TabRowControl.h"
|
||||
#include "AdminWarningPlaceholder.h"
|
||||
#include "ColorHelper.h"
|
||||
#include "DebugTapConnection.h"
|
||||
#include "SettingsTab.h"
|
||||
|
|
|
@ -63,6 +63,9 @@
|
|||
<Page Include="CommandPalette.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="AdminWarningPlaceholder.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
|
@ -141,6 +144,9 @@
|
|||
<ClInclude Include="AppLogic.h">
|
||||
<DependentUpon>AppLogic.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AdminWarningPlaceholder.h">
|
||||
<DependentUpon>AdminWarningPlaceholder.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
|
@ -234,6 +240,9 @@
|
|||
<ClCompile Include="AppLogic.cpp">
|
||||
<DependentUpon>AppLogic.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AdminWarningPlaceholder.cpp">
|
||||
<DependentUpon>AdminWarningPlaceholder.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="Toast.cpp" />
|
||||
</ItemGroup>
|
||||
|
@ -295,6 +304,10 @@
|
|||
<DependentUpon>CommandPalette.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="AdminWarningPlaceholder.idl">
|
||||
<DependentUpon>AdminWarningPlaceholder.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="FilteredCommand.idl" />
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "DebugTapConnection.h"
|
||||
#include "SettingsTab.h"
|
||||
#include "RenameWindowRequestedArgs.g.cpp"
|
||||
#include "AdminWarningPlaceholder.h"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
|
||||
#include <til/latch.h>
|
||||
|
@ -1415,21 +1416,24 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
uint32_t realRowsToScroll;
|
||||
if (rowsToScroll == nullptr)
|
||||
if (const auto& termControl{ terminalTab->GetActiveTerminalControl() })
|
||||
{
|
||||
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
|
||||
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
|
||||
terminalTab->GetActiveTerminalControl().ViewHeight() :
|
||||
_systemRowsToScroll;
|
||||
uint32_t realRowsToScroll;
|
||||
if (rowsToScroll == nullptr)
|
||||
{
|
||||
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
|
||||
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
|
||||
termControl.ViewHeight() :
|
||||
_systemRowsToScroll;
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the custom value specified in the command
|
||||
realRowsToScroll = rowsToScroll.Value();
|
||||
}
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll);
|
||||
terminalTab->Scroll(scrollDelta);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use the custom value specified in the command
|
||||
realRowsToScroll = rowsToScroll.Value();
|
||||
}
|
||||
auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll);
|
||||
terminalTab->Scroll(scrollDelta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1483,6 +1487,246 @@ namespace winrt::TerminalApp::implementation
|
|||
return true;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Returns true if this commandline is a commandline that we know is safe.
|
||||
// Generally, this is true for any executables in system32. We can use
|
||||
// this to bypass the elevated state check, because we're confident that
|
||||
// executables in that path won't have been hijacked.
|
||||
// - TECHNICALLY a user can take ownership of a file in system32 and
|
||||
// replace it as the system administrator. You could say it's OK though
|
||||
// because you'd already have to have had admin rights to mess that
|
||||
// folder up or something.
|
||||
// - Will attempt to resolve environment strings.
|
||||
// - Will also manually allow commandlines as generated for the default WSL
|
||||
// distros.
|
||||
// - Will also trust %ProgramFiles%\Powershell\...\pwsh.exe paths, for
|
||||
// PowerShell Core.
|
||||
// Arguments:
|
||||
// - commandLine: the command to check.
|
||||
// Return Value (example):
|
||||
// - C:\windows\system32\cmd.exe -> returns true
|
||||
// - cmd.exe -> returns false
|
||||
// - C:\windows\system32\cmd.exe /k echo sneaky sneak -> returns false
|
||||
// - %SystemRoot%\System32\cmd.exe -> returns true
|
||||
// - %SystemRoot%\System32\wsl.exe -d <distro name> -> returns true
|
||||
bool TerminalPage::_isTrustedCommandline(std::wstring_view commandLine)
|
||||
{
|
||||
// use C++11 magic statics to make sure we only do this once.
|
||||
static std::wstring systemDirectory = []() -> std::wstring {
|
||||
// *** THIS IS A SINGLETON ***
|
||||
static std::wstring sys32{};
|
||||
if (FAILED(wil::GetSystemDirectoryW(sys32)))
|
||||
{
|
||||
// we couldn't look up where system32 is?? Then it's definitely not
|
||||
// in System32
|
||||
return {};
|
||||
}
|
||||
return sys32;
|
||||
}();
|
||||
|
||||
if (systemDirectory.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::wstring fullCommandline{
|
||||
wil::ExpandEnvironmentStringsW<std::wstring>(commandLine.data())
|
||||
};
|
||||
|
||||
if (fullCommandline.size() > systemDirectory.size())
|
||||
{
|
||||
// Get the first part of the executable path
|
||||
const auto start = fullCommandline.substr(0, systemDirectory.size());
|
||||
// Doing this as an ASCII only check might be wrong, but I'm
|
||||
// guessing if system32 isn't at X:\windows\system32... this isn't
|
||||
// the only thing that is going to be sad in Windows.
|
||||
const auto pathEquals = til::equals_insensitive_ascii(start, systemDirectory);
|
||||
if (pathEquals && std::filesystem::exists(std::filesystem::path{ fullCommandline }))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We're explicitly not auto-allowing wsl.exe -d <distro name>. It's
|
||||
// trivial to insert some malicious stuff into WSL, via .bash_profile,
|
||||
// so we're not giving them the (y)
|
||||
|
||||
// But we do want to allow `pwsh.exe` profiles in the expected place to
|
||||
// work.
|
||||
const std::vector<std::filesystem::path> powershellCoreRoots
|
||||
{
|
||||
// Always look in "%LOCALAPPDATA%\Microsoft\WindowsApps", which is
|
||||
// where the pwsh.exe execution alias lives.
|
||||
{ wil::ExpandEnvironmentStringsW<std::wstring>(L"%LOCALAPPDATA%\\Microsoft\\WindowsApps") },
|
||||
|
||||
// Always look in "%ProgramFiles%\PowerShell"
|
||||
{ wil::ExpandEnvironmentStringsW<std::wstring>(L"%ProgramFiles%\\PowerShell") },
|
||||
|
||||
#if defined(_M_AMD64) || defined(_M_ARM64) // No point in looking for WOW if we're not somewhere it exists
|
||||
{ wil::ExpandEnvironmentStringsW<std::wstring>(L"%ProgramFiles(x86)%\\PowerShell") },
|
||||
#endif
|
||||
|
||||
#if defined(_M_ARM64) // same with ARM
|
||||
{
|
||||
wil::ExpandEnvironmentStringsW<std::wstring>(L"%ProgramFiles(Arm)%\\PowerShell")
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Is the filename for this commandline `pwsh.exe`?
|
||||
const std::filesystem::path exePath{ fullCommandline };
|
||||
const auto endsWithPwsh{ exePath.filename() == L"pwsh.exe" };
|
||||
// We'll also need to check the parent path, so make sure it has one here.
|
||||
|
||||
if (endsWithPwsh && exePath.has_parent_path())
|
||||
{
|
||||
const auto parentPath{ exePath.parent_path() };
|
||||
for (const auto& pwshRoot : powershellCoreRoots)
|
||||
{
|
||||
// Does the commandline start with this root, and end with pwsh.exe?
|
||||
const auto startsWithRoot{ til::starts_with(fullCommandline, pwshRoot.c_str()) };
|
||||
|
||||
// Is either the immediate parent, or the grandparent, this root exactly?
|
||||
//
|
||||
// We need to check the grandparent for the
|
||||
// `%ProgramFiles%\\PowerShell\\7\\pwsh.exe` case.
|
||||
const auto parentIsCorrect = (parentPath == pwshRoot) ||
|
||||
(parentPath.has_parent_path() && parentPath.parent_path() == pwshRoot);
|
||||
|
||||
if (startsWithRoot && parentIsCorrect)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - For a given commandline, determines if we should prompt the user for
|
||||
// approval. We only do this check when elevated. This will check the
|
||||
// AllowedCommandlines in `elevated-state.json`, to see if the commandline
|
||||
// already exists in that list.
|
||||
// Arguments:
|
||||
// - cmdline: The commandline to check
|
||||
// Return Value:
|
||||
// - true if we should prompt the user for approval.
|
||||
bool TerminalPage::_shouldPromptForCommandline(const winrt::hstring& cmdline) const
|
||||
{
|
||||
// NOTE: For debugging purposes, changing this to `true || IsElevated()`
|
||||
// is a handy way of forcing the elevation logic, even when unelevated.
|
||||
if (IsElevated())
|
||||
{
|
||||
// If the cmdline is EXACTLY an executable in
|
||||
// `C:\WINDOWS\System32`, then ignore this check.
|
||||
if (_isTrustedCommandline(cmdline))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const auto& allowedCommandlines{ ApplicationState::SharedInstance().AllowedCommandlines() })
|
||||
{
|
||||
for (const auto& approved : allowedCommandlines)
|
||||
{
|
||||
if (approved == cmdline)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void TerminalPage::_adminWarningPrimaryClicked(const TerminalApp::AdminWarningPlaceholder& sender,
|
||||
const winrt::Windows::UI::Xaml::RoutedEventArgs& /*args*/)
|
||||
{
|
||||
auto warningControl{ winrt::get_self<AdminWarningPlaceholder>(sender) };
|
||||
const auto& cmdline{ warningControl->Commandline() };
|
||||
// Look through the tabs and panes to look for us. Whichever pane had us
|
||||
// as content - replace their content with the TermControl we were
|
||||
// holding on to.
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
if (const auto& tabImpl{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
tabImpl->GetRootPane()->WalkTree([warningControl, cmdline, tabImpl](std::shared_ptr<Pane> pane) -> bool {
|
||||
const auto& projectedWarningControl{ pane->GetUserControl().try_as<TerminalApp::AdminWarningPlaceholder>() };
|
||||
// If it was a warning control, then get our implementation
|
||||
// type out of it.
|
||||
if (const auto& otherWarning{ winrt::get_self<AdminWarningPlaceholder>(projectedWarningControl) })
|
||||
{
|
||||
// This pane had a warning in it.
|
||||
// Was it a warning for the same commandline that we
|
||||
// just approved?
|
||||
if (otherWarning->Commandline() == cmdline)
|
||||
{
|
||||
// Go ahead and allow them. Swap the control into
|
||||
// the pane, which will initialize and start it.
|
||||
tabImpl->ReplaceControl(pane, otherWarning->Control());
|
||||
}
|
||||
// Don't return true here. We want to make sure to check
|
||||
// all the panes for the same commandline we just
|
||||
// approved.
|
||||
}
|
||||
// return false so we make sure to iterate on every leaf.
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update the list of approved commandlines.
|
||||
auto allowedCommandlines{ ApplicationState::SharedInstance().AllowedCommandlines() };
|
||||
if (!allowedCommandlines)
|
||||
{
|
||||
allowedCommandlines = winrt::single_threaded_vector<winrt::hstring>();
|
||||
}
|
||||
|
||||
// But of course, we don't need to add this commandline if it's already
|
||||
// in the list of approved commandlines.
|
||||
bool foundCopy = false;
|
||||
for (const auto& approved : allowedCommandlines)
|
||||
{
|
||||
if (approved == cmdline)
|
||||
{
|
||||
foundCopy = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundCopy)
|
||||
{
|
||||
allowedCommandlines.Append(cmdline);
|
||||
}
|
||||
ApplicationState::SharedInstance().AllowedCommandlines(allowedCommandlines);
|
||||
}
|
||||
|
||||
void TerminalPage::_adminWarningCancelClicked(const TerminalApp::AdminWarningPlaceholder& sender,
|
||||
const winrt::Windows::UI::Xaml::RoutedEventArgs& /*args*/)
|
||||
{
|
||||
auto warningControl{ winrt::get_self<AdminWarningPlaceholder>(sender) };
|
||||
|
||||
for (const auto& tab : _tabs)
|
||||
{
|
||||
if (const auto& tabImpl{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
tabImpl->GetRootPane()->WalkTree([warningControl](std::shared_ptr<Pane> pane) -> bool {
|
||||
if (pane->GetUserControl() == *warningControl)
|
||||
{
|
||||
pane->Close();
|
||||
return true;
|
||||
}
|
||||
// We're not going to auto-close all the other panes with
|
||||
// the same commandline warning, akin to what we're doing in
|
||||
// the approve handler. If they want to reject one pane, but
|
||||
// accept the next one, that's okay.
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Split the focused pane either horizontally or vertically, and place the
|
||||
// given pane accordingly in the tree
|
||||
|
@ -1852,8 +2096,10 @@ namespace winrt::TerminalApp::implementation
|
|||
if (warnMultiLine)
|
||||
{
|
||||
const auto focusedTab = _GetFocusedTabImpl();
|
||||
const auto& termControl{ focusedTab->GetActiveTerminalControl() };
|
||||
// Do not warn about multi line pasting if the current tab has bracketed paste enabled.
|
||||
warnMultiLine = warnMultiLine && !focusedTab->GetActiveTerminalControl().BracketedPasteEnabled();
|
||||
warnMultiLine = warnMultiLine &&
|
||||
(termControl && !termControl.BracketedPasteEnabled());
|
||||
}
|
||||
|
||||
// We have to initialize the dialog here to be able to change the text of the text block within it
|
||||
|
@ -2161,11 +2407,14 @@ namespace winrt::TerminalApp::implementation
|
|||
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
|
||||
profile = GetClosestProfileForDuplicationOfProfile(profile);
|
||||
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);
|
||||
const auto workingDirectory = focusedTab->GetActiveTerminalControl().WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
if (const auto& control{ focusedTab->GetActiveTerminalControl() })
|
||||
{
|
||||
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
const auto workingDirectory = control.WorkingDirectory();
|
||||
const auto validWorkingDirectory = !workingDirectory.empty();
|
||||
if (validWorkingDirectory)
|
||||
{
|
||||
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2199,7 +2448,21 @@ namespace winrt::TerminalApp::implementation
|
|||
const auto control = _InitControl(controlSettings, connection);
|
||||
_RegisterTerminalEvents(control);
|
||||
|
||||
auto resultPane = std::make_shared<Pane>(profile, control);
|
||||
WUX::Controls::UserControl controlToAdd{ control };
|
||||
|
||||
// Check if we should warn the user about running a new unelevated
|
||||
// commandline.
|
||||
const auto& cmdline{ controlSettings.DefaultSettings().Commandline() };
|
||||
const auto doAdminWarning{ _shouldPromptForCommandline(cmdline) };
|
||||
if (doAdminWarning)
|
||||
{
|
||||
auto warningControl{ winrt::make_self<implementation::AdminWarningPlaceholder>(control, cmdline) };
|
||||
warningControl->PrimaryButtonClicked({ get_weak(), &TerminalPage::_adminWarningPrimaryClicked });
|
||||
warningControl->CancelButtonClicked({ get_weak(), &TerminalPage::_adminWarningCancelClicked });
|
||||
controlToAdd = *warningControl;
|
||||
}
|
||||
|
||||
auto resultPane = std::make_shared<Pane>(profile, controlToAdd);
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
|
@ -2220,6 +2483,17 @@ namespace winrt::TerminalApp::implementation
|
|||
original->SetActive();
|
||||
}
|
||||
|
||||
if (doAdminWarning)
|
||||
{
|
||||
// We know this is safe - we literally just added the
|
||||
// AdminWarningPlaceholder as the controlToAdd like 20 lines up.
|
||||
//
|
||||
// Focus the warning here. The LayoutUpdated within the dialog
|
||||
// itself isn't good enough. That, for some reason, fires _before_
|
||||
// the dialog is in the UI tree, which is useless for us.
|
||||
controlToAdd.try_as<implementation::AdminWarningPlaceholder>()->FocusOnLaunch();
|
||||
}
|
||||
|
||||
return resultPane;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace TerminalAppLocalTests
|
|||
{
|
||||
class TabTests;
|
||||
class SettingsTests;
|
||||
class TrustCommandlineTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
|
@ -207,6 +208,7 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseReadOnlyDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowMultiLinePasteWarningDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowLargePasteWarningDialog();
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCommandlineApproveWarning();
|
||||
|
||||
void _CreateNewTabFlyout();
|
||||
void _OpenNewTabDropdown();
|
||||
|
@ -403,6 +405,12 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetClosestProfileForDuplicationOfProfile(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile) const noexcept;
|
||||
|
||||
bool _shouldPromptForCommandline(const winrt::hstring& cmdline) const;
|
||||
void _adminWarningPrimaryClicked(const winrt::TerminalApp::AdminWarningPlaceholder& sender,
|
||||
const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void _adminWarningCancelClicked(const winrt::TerminalApp::AdminWarningPlaceholder& sender,
|
||||
const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
|
||||
winrt::fire_and_forget _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
|
||||
void _CloseOnExitInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
|
||||
void _KeyboardServiceWarningInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
|
||||
|
@ -410,6 +418,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void _SetAsDefaultOpenSettingsHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
|
||||
static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
|
||||
static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
|
||||
static bool _isTrustedCommandline(std::wstring_view commandLine);
|
||||
|
||||
#pragma region ActionHandlers
|
||||
// These are all defined in AppActionHandlers.cpp
|
||||
|
@ -420,6 +429,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
friend class TerminalAppLocalTests::TabTests;
|
||||
friend class TerminalAppLocalTests::SettingsTests;
|
||||
friend class TerminalAppLocalTests::TrustCommandlineTests;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -426,7 +426,10 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::fire_and_forget TerminalTab::Scroll(const int delta)
|
||||
{
|
||||
auto control = GetActiveTerminalControl();
|
||||
|
||||
if (!control)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
co_await winrt::resume_foreground(control.Dispatcher());
|
||||
|
||||
const auto currentOffset = control.ScrollOffset();
|
||||
|
@ -511,7 +514,11 @@ namespace winrt::TerminalApp::implementation
|
|||
if (p->_IsLeaf())
|
||||
{
|
||||
p->Id(_nextPaneId);
|
||||
_AttachEventHandlersToControl(p->Id().value(), p->_control);
|
||||
if (auto termControl{ p->_control.try_as<TermControl>() })
|
||||
{
|
||||
_AttachEventHandlersToControl(p->Id().value(), termControl);
|
||||
}
|
||||
|
||||
_nextPaneId++;
|
||||
}
|
||||
return false;
|
||||
|
@ -856,6 +863,10 @@ namespace winrt::TerminalApp::implementation
|
|||
// - <none>
|
||||
void TerminalTab::_AttachEventHandlersToControl(const uint32_t paneId, const TermControl& control)
|
||||
{
|
||||
if (!control)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto weakThis{ get_weak() };
|
||||
auto dispatcher = TabViewItem().Dispatcher();
|
||||
ControlEventTokens events{};
|
||||
|
@ -1744,6 +1755,19 @@ namespace winrt::TerminalApp::implementation
|
|||
return Title();
|
||||
}
|
||||
|
||||
void TerminalTab::ReplaceControl(std::shared_ptr<Pane> pane, const Controls::UserControl& control)
|
||||
{
|
||||
pane->ReplaceControl(control);
|
||||
|
||||
if (auto termControl{ pane->_control.try_as<TermControl>() })
|
||||
{
|
||||
_AttachEventHandlersToControl(pane->Id().value(), termControl);
|
||||
}
|
||||
|
||||
// Update the title manually.
|
||||
UpdateTitle();
|
||||
}
|
||||
|
||||
DEFINE_EVENT(TerminalTab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
|
||||
DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
|
||||
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
|
||||
|
|
|
@ -93,6 +93,9 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
std::shared_ptr<Pane> GetRootPane() const { return _rootPane; }
|
||||
|
||||
void ReplaceControl(std::shared_ptr<Pane> pane,
|
||||
const winrt::Windows::UI::Xaml::Controls::UserControl& control);
|
||||
|
||||
winrt::TerminalApp::TerminalTabStatus TabStatus()
|
||||
{
|
||||
return _tabStatus;
|
||||
|
|
|
@ -2645,4 +2645,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
return _core.ReadEntireBuffer();
|
||||
}
|
||||
|
||||
Media::Brush TermControl::BackgroundBrush()
|
||||
{
|
||||
return RootGrid().Background();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
hstring ReadEntireBuffer() const;
|
||||
|
||||
Windows::UI::Xaml::Media::Brush BackgroundBrush();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
|
|
|
@ -71,5 +71,7 @@ namespace Microsoft.Terminal.Control
|
|||
void ToggleReadOnly();
|
||||
|
||||
String ReadEntireBuffer();
|
||||
|
||||
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue