Compare commits
31 commits
main
...
dev/migrie
Author | SHA1 | Date | |
---|---|---|---|
3bfe3d1d88 | |||
d8bd527996 | |||
81a5a736fe | |||
dbee7e6d67 | |||
36fb572308 | |||
6b6ca8fe60 | |||
015e3211fc | |||
5c17603a94 | |||
354e4b00a3 | |||
8707c03715 | |||
31b2763be5 | |||
a000d81fa9 | |||
d36a08186c | |||
3b1bb455d8 | |||
901bc78966 | |||
4150609b42 | |||
d5920a8c69 | |||
fd364db727 | |||
3a0fbd9f59 | |||
64533c838a | |||
aa6b08118f | |||
b0b44410c6 | |||
b541179333 | |||
56f1223dc5 | |||
88d974280d | |||
d84a31801a | |||
6910677a11 | |||
9331cc8e59 | |||
2f64db2765 | |||
4cc3d39de9 | |||
d8dcb6f570 |
|
@ -3,9 +3,11 @@
|
|||
|
||||
#include "pch.h"
|
||||
#include "MyPage.h"
|
||||
#include "MySettings.h"
|
||||
#include <LibraryResources.h>
|
||||
#include "MyPage.g.cpp"
|
||||
#include "MySettings.h"
|
||||
#include "..\..\..\src\cascadia\UnitTests_Control\MockControlSettings.h"
|
||||
#include "..\..\..\src\types\inc\utils.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
|
@ -26,7 +28,7 @@ namespace winrt::SampleApp::implementation
|
|||
|
||||
void MyPage::Create()
|
||||
{
|
||||
auto settings = winrt::make_self<implementation::MySettings>();
|
||||
auto settings = winrt::make_self<MySettings>();
|
||||
|
||||
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
|
||||
winrt::hstring{},
|
||||
|
@ -44,6 +46,212 @@ namespace winrt::SampleApp::implementation
|
|||
Control::TermControl control{ *settings, conn };
|
||||
|
||||
InProcContent().Children().Append(control);
|
||||
|
||||
// Once the control loads (and not before that), write some text for debugging:
|
||||
control.Initialized([conn](auto&&, auto&&) {
|
||||
conn.WriteInput(L"This TermControl is hosted in-proc...");
|
||||
});
|
||||
}
|
||||
|
||||
static wil::unique_process_information _createHostClassProcess(const winrt::guid& g)
|
||||
{
|
||||
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(g) };
|
||||
|
||||
// Create an event that the content process will use to signal it is
|
||||
// ready to go. We won't need the event after this function, so the
|
||||
// unique_event will clean up our handle when we leave this scope. The
|
||||
// ContentProcess is responsible for cleaning up its own handle.
|
||||
wil::unique_event ev{ CreateEvent(nullptr, true, false, L"contentProcessStarted") };
|
||||
// Make sure to mark this handle as inheritable! Even with
|
||||
// bInheritHandles=true, this is only inherited when it's explicitly
|
||||
// allowed to be.
|
||||
SetHandleInformation(ev.get(), HANDLE_FLAG_INHERIT, 1);
|
||||
|
||||
// god bless, fmt::format will format a HANDLE like `0xa80`
|
||||
std::wstring commandline{
|
||||
fmt::format(L"windowsterminal.exe --content {} --signal {}", guidStr, ev.get())
|
||||
};
|
||||
|
||||
STARTUPINFO siOne{ 0 };
|
||||
siOne.cb = sizeof(STARTUPINFOW);
|
||||
wil::unique_process_information piOne;
|
||||
auto succeeded = CreateProcessW(
|
||||
nullptr,
|
||||
commandline.data(),
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
true, // bInheritHandles
|
||||
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
||||
nullptr, // lpEnvironment
|
||||
nullptr, // startingDirectory
|
||||
&siOne, // lpStartupInfo
|
||||
&piOne // lpProcessInformation
|
||||
);
|
||||
THROW_IF_WIN32_BOOL_FALSE(succeeded);
|
||||
|
||||
// Wait for the child process to signal that they're ready.
|
||||
WaitForSingleObject(ev.get(), INFINITE);
|
||||
|
||||
return std::move(piOne);
|
||||
}
|
||||
|
||||
winrt::fire_and_forget MyPage::_writeToLog(std::wstring_view str)
|
||||
{
|
||||
winrt::hstring copy{ str };
|
||||
// Switch back to the UI thread.
|
||||
co_await resume_foreground(Dispatcher());
|
||||
winrt::WUX::Controls::TextBlock block;
|
||||
block.Text(copy);
|
||||
Log().Children().Append(block);
|
||||
}
|
||||
|
||||
winrt::fire_and_forget MyPage::CreateClicked(const IInspectable& sender,
|
||||
const WUX::Input::TappedRoutedEventArgs& eventArgs)
|
||||
{
|
||||
auto guidString = GuidInput().Text();
|
||||
|
||||
// Capture calling context.
|
||||
winrt::apartment_context ui_thread;
|
||||
|
||||
auto canConvert = guidString.size() == 38 &&
|
||||
guidString.front() == '{' &&
|
||||
guidString.back() == '}';
|
||||
bool tryingToAttach = false;
|
||||
winrt::guid contentGuid{ ::Microsoft::Console::Utils::CreateGuid() };
|
||||
|
||||
if (canConvert)
|
||||
{
|
||||
GUID result{};
|
||||
if (SUCCEEDED(IIDFromString(guidString.c_str(), &result)))
|
||||
{
|
||||
contentGuid = result;
|
||||
tryingToAttach = true;
|
||||
}
|
||||
}
|
||||
_writeToLog(tryingToAttach ? L"Attaching to existing content process" : L"Creating new content process");
|
||||
|
||||
co_await winrt::resume_background();
|
||||
if (!tryingToAttach)
|
||||
{
|
||||
// Spawn a wt.exe, with the guid on the commandline
|
||||
piContentProcess = std::move(_createHostClassProcess(contentGuid));
|
||||
}
|
||||
|
||||
// THIS MUST TAKE PLACE AFTER _createHostClassProcess.
|
||||
// * If we're creating a new OOP control, _createHostClassProcess will
|
||||
// spawn the process that will actually host the ContentProcess
|
||||
// object.
|
||||
// * If we're attaching, then that process already exists.
|
||||
Control::ContentProcess content{nullptr};
|
||||
try
|
||||
{
|
||||
content = create_instance<Control::ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
|
||||
}
|
||||
catch (winrt::hresult_error hr)
|
||||
{
|
||||
_writeToLog(L"CreateInstance the ContentProces object");
|
||||
_writeToLog(fmt::format(L" HR ({}): {}", hr.code(), hr.message().c_str()));
|
||||
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
|
||||
if (content == nullptr)
|
||||
{
|
||||
_writeToLog(L"Failed to connect to the ContentProces object. It may not have been started fast enough.");
|
||||
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
|
||||
TerminalConnection::ConnectionInformation connectInfo{ nullptr };
|
||||
Control::IControlSettings settings{ *winrt::make_self<implementation::MySettings>() };
|
||||
|
||||
// When creating a terminal for the first time, pass it a connection
|
||||
// info
|
||||
//
|
||||
// otherwise, when attaching to an existing one, just pass null, because
|
||||
// we don't need the connection info.
|
||||
if (!tryingToAttach)
|
||||
{
|
||||
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted out-of-proc...",
|
||||
winrt::hstring{},
|
||||
L"",
|
||||
nullptr,
|
||||
32,
|
||||
80,
|
||||
winrt::guid()) };
|
||||
|
||||
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
|
||||
winrt::hstring myClass{ winrt::name_of<TerminalConnection::ConptyConnection>() };
|
||||
connectInfo = TerminalConnection::ConnectionInformation(myClass, connectionSettings);
|
||||
|
||||
if (!content.Initialize(settings, connectInfo))
|
||||
{
|
||||
_writeToLog(L"Failed to Initialize the ContentProces object.");
|
||||
co_return; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're attaching, we don't really need to do anything special.
|
||||
}
|
||||
|
||||
// Switch back to the UI thread.
|
||||
co_await ui_thread;
|
||||
|
||||
// Create the XAML control that will be attached to the content process.
|
||||
// We're not passing in a connection, because the contentGuid will be used instead.
|
||||
Control::TermControl control{ contentGuid, settings, nullptr };
|
||||
control.RaiseNotice([this](auto&&, auto& args) {
|
||||
_writeToLog(L"Content process died, probably.");
|
||||
_writeToLog(args.Message());
|
||||
OutOfProcContent().Children().Clear();
|
||||
GuidInput().Text(L"");
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
piContentProcess.reset();
|
||||
}
|
||||
});
|
||||
control.ConnectionStateChanged([this, control](auto&&, auto&) {
|
||||
const auto newConnectionState = control.ConnectionState();
|
||||
if (newConnectionState == TerminalConnection::ConnectionState::Closed)
|
||||
{
|
||||
_writeToLog(L"Connection was closed");
|
||||
OutOfProcContent().Children().Clear();
|
||||
GuidInput().Text(L"");
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
piContentProcess.reset();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Log().Children().Clear();
|
||||
OutOfProcContent().Children().Append(control);
|
||||
|
||||
if (!tryingToAttach)
|
||||
{
|
||||
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(contentGuid) };
|
||||
GuidInput().Text(guidStr);
|
||||
}
|
||||
}
|
||||
|
||||
void MyPage::CloseClicked(const IInspectable& /*sender*/,
|
||||
const WUX::Input::TappedRoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
OutOfProcContent().Children().Clear();
|
||||
GuidInput().Text(L"");
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
piContentProcess.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void MyPage::KillClicked(const IInspectable& /*sender*/,
|
||||
const WUX::Input::TappedRoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
if (piContentProcess.hProcess)
|
||||
{
|
||||
TerminateProcess(piContentProcess.hProcess, (UINT)-1);
|
||||
piContentProcess.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
|
|
@ -14,11 +14,18 @@ namespace winrt::SampleApp::implementation
|
|||
MyPage();
|
||||
|
||||
void Create();
|
||||
|
||||
hstring Title();
|
||||
|
||||
winrt::fire_and_forget CreateClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
|
||||
void CloseClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
|
||||
void KillClicked(const IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& eventArgs);
|
||||
|
||||
private:
|
||||
friend struct MyPageT<MyPage>; // for Xaml to bind events
|
||||
|
||||
wil::unique_process_information piContentProcess;
|
||||
|
||||
winrt::fire_and_forget _writeToLog(std::wstring_view str);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,23 @@
|
|||
<TextBox x:Name="GuidInput"
|
||||
Width="400"
|
||||
PlaceholderText="{}{guid here}" />
|
||||
<Button Grid.Row="0">
|
||||
<Button x:Name="CreateOopControl"
|
||||
Grid.Row="0"
|
||||
Tapped="CreateClicked">
|
||||
Create
|
||||
</Button>
|
||||
<Button x:Name="CloseOopControl"
|
||||
Grid.Row="0"
|
||||
Margin="4,0,0,0"
|
||||
Tapped="CloseClicked">
|
||||
Close
|
||||
</Button>
|
||||
<Button x:Name="KillOopControl"
|
||||
Grid.Row="0"
|
||||
Margin="4,0,0,0"
|
||||
Tapped="KillClicked">
|
||||
Kill
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
|
@ -46,14 +60,26 @@
|
|||
VerticalAlignment="Stretch"
|
||||
Background="#ff0000" />
|
||||
|
||||
<Grid x:Name="OutOfProcContent"
|
||||
Grid.Column="1"
|
||||
Padding="16"
|
||||
<Grid Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="#0000ff" />
|
||||
VerticalAlignment="Stretch">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel x:Name="Log"
|
||||
Grid.Row="0"
|
||||
Orientation="Vertical" />
|
||||
|
||||
<Grid x:Name="OutOfProcContent"
|
||||
Grid.Row="1"
|
||||
Padding="16"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="#0000ff" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@ Licensed under the MIT license.
|
|||
#include <conattrs.hpp>
|
||||
#include "MySettings.g.h"
|
||||
|
||||
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, uint32_t>;
|
||||
using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
|
||||
|
||||
namespace winrt::SampleApp::implementation
|
||||
{
|
||||
struct MySettings : MySettingsT<MySettings>
|
||||
|
@ -41,6 +44,8 @@ namespace winrt::SampleApp::implementation
|
|||
winrt::Microsoft::Terminal::Core::ICoreAppearance UnfocusedAppearance() { return {}; };
|
||||
|
||||
WINRT_PROPERTY(bool, TrimBlockSelection, false);
|
||||
WINRT_PROPERTY(bool, DetectURLs, true);
|
||||
WINRT_PROPERTY(bool, IntenseIsBright, true);
|
||||
// ------------------------ End of Core Settings -----------------------
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, ProfileName);
|
||||
|
@ -78,7 +83,10 @@ namespace winrt::SampleApp::implementation
|
|||
|
||||
WINRT_PROPERTY(winrt::hstring, PixelShaderPath);
|
||||
|
||||
WINRT_PROPERTY(bool, DetectURLs, true);
|
||||
WINRT_PROPERTY(IFontFeatureMap, FontFeatures);
|
||||
WINRT_PROPERTY(IFontAxesMap, FontAxes);
|
||||
|
||||
WINRT_PROPERTY(bool, IntenseIsBold, true);
|
||||
|
||||
private:
|
||||
std::array<winrt::Microsoft::Terminal::Core::Color, COLOR_TABLE_SIZE> _ColorTable;
|
||||
|
|
|
@ -856,4 +856,24 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
};
|
||||
_forAllPeasantsIgnoringTheDead(callback, onError);
|
||||
}
|
||||
|
||||
void Monarch::RequestMovePane(winrt::hstring window,
|
||||
winrt::guid contentGuid,
|
||||
uint32_t tabIndex)
|
||||
{
|
||||
auto windowId = _lookupPeasantIdForName(window);
|
||||
if (windowId == 0)
|
||||
{ /* TODO! try the name as an integer ID */
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto targetPeasant{ _getPeasant(windowId) })
|
||||
{
|
||||
auto request = winrt::make_self<implementation::AttachRequest>(contentGuid, tabIndex);
|
||||
targetPeasant.AttachPaneToWindow(*request);
|
||||
}
|
||||
else
|
||||
{ /*TODO! log */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
|
||||
|
||||
void RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace Microsoft.Terminal.Remoting
|
|||
void SummonAllWindows();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames { get; };
|
||||
|
||||
void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "SummonWindowBehavior.h"
|
||||
#include "Peasant.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "AttachRequest.g.cpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
|
@ -257,4 +258,21 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::AttachPaneToWindow(Remoting::AttachRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
_AttachRequestedHandlers(*this, request);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_AttachPaneToWindow",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "Peasant.g.h"
|
||||
#include "../cascadia/inc/cppwinrt_utils.h"
|
||||
#include "RenameRequestArgs.h"
|
||||
#include "AttachRequest.g.h"
|
||||
|
||||
namespace RemotingUnitTests
|
||||
{
|
||||
|
@ -13,6 +14,17 @@ namespace RemotingUnitTests
|
|||
};
|
||||
namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
struct AttachRequest : public AttachRequestT<AttachRequest>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::guid, ContentGuid);
|
||||
WINRT_PROPERTY(uint32_t, TabIndex);
|
||||
|
||||
public:
|
||||
AttachRequest(winrt::guid contentGuid,
|
||||
uint32_t tabIndex) :
|
||||
_ContentGuid{ contentGuid }, _TabIndex{ tabIndex } {};
|
||||
};
|
||||
|
||||
struct Peasant : public PeasantT<Peasant>
|
||||
{
|
||||
Peasant();
|
||||
|
@ -31,6 +43,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
|
||||
void AttachPaneToWindow(Remoting::AttachRequest request);
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
|
||||
|
||||
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
|
||||
|
@ -44,6 +58,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
TYPED_EVENT(AttachRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest);
|
||||
|
||||
private:
|
||||
Peasant(const uint64_t testPID);
|
||||
|
|
|
@ -47,6 +47,11 @@ namespace Microsoft.Terminal.Remoting
|
|||
MonitorBehavior ToMonitor;
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass AttachRequest {
|
||||
Guid ContentGuid { get; };
|
||||
UInt32 TabIndex { get; };
|
||||
}
|
||||
|
||||
interface IPeasant
|
||||
{
|
||||
CommandlineArgs InitialArgs { get; };
|
||||
|
@ -66,6 +71,7 @@ namespace Microsoft.Terminal.Remoting
|
|||
void Summon(SummonWindowBehavior behavior);
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
void AttachPaneToWindow(AttachRequest request);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
|
||||
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
|
||||
|
@ -75,6 +81,7 @@ namespace Microsoft.Terminal.Remoting
|
|||
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, AttachRequest> AttachRequested;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Peasant : IPeasant
|
||||
|
|
|
@ -564,4 +564,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
return false;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget WindowManager::RequestMovePane(winrt::hstring window,
|
||||
winrt::guid contentGuid,
|
||||
uint32_t tabIndex)
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
_monarch.RequestMovePane(window, contentGuid, tabIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
winrt::fire_and_forget RequestShowTrayIcon();
|
||||
winrt::fire_and_forget RequestHideTrayIcon();
|
||||
bool DoesQuakeWindowExist();
|
||||
winrt::fire_and_forget RequestMovePane(winrt::hstring window, winrt::guid contentGuid, uint32_t tabIndex);
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
|
|
@ -17,6 +17,9 @@ namespace Microsoft.Terminal.Remoting
|
|||
void RequestHideTrayIcon();
|
||||
Boolean DoesQuakeWindowExist();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames();
|
||||
|
||||
void RequestMovePane(String window, Guid contentGuid, UInt32 tabIndex);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
|
|
|
@ -152,7 +152,7 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
else if (const auto& realArgs = args.ActionArgs().try_as<MovePaneArgs>())
|
||||
{
|
||||
auto moved = _MovePane(realArgs.TabIndex());
|
||||
auto moved = _MovePane(realArgs);
|
||||
args.Handled(moved);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1477,4 +1477,12 @@ namespace winrt::TerminalApp::implementation
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AppLogic::AttachPane(winrt::guid contentGuid, uint32_t tabIndex)
|
||||
{
|
||||
if (_root)
|
||||
{
|
||||
_root->AttachPane(contentGuid, tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
bool GetMinimizeToTray();
|
||||
bool GetAlwaysShowTrayIcon();
|
||||
void AttachPane(winrt::guid contentGuid, uint32_t tabIndex);
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
|
||||
|
@ -171,6 +172,7 @@ namespace winrt::TerminalApp::implementation
|
|||
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
|
||||
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
|
||||
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
|
||||
FORWARDED_TYPED_EVENT(RequestMovePane, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMovePaneArgs, _root, RequestMovePane);
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class TerminalAppLocalTests::CommandlineTest;
|
||||
|
|
|
@ -74,6 +74,7 @@ namespace TerminalApp
|
|||
Boolean GetAlwaysShowTrayIcon();
|
||||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
void AttachPane(Guid contentGuid, UInt32 tabIndex);
|
||||
|
||||
Windows.Foundation.Collections.IMapView<Microsoft.Terminal.Control.KeyChord, Microsoft.Terminal.Settings.Model.Command> GlobalHotkeys();
|
||||
|
||||
|
@ -95,5 +96,6 @@ namespace TerminalApp
|
|||
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestMovePaneArgs> RequestMovePane;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,39 +56,42 @@ namespace winrt::TerminalApp::implementation
|
|||
// - existingConnection: An optional connection that is already established to a PTY
|
||||
// for this tab to host instead of creating one.
|
||||
// If not defined, the tab will create the connection.
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs,
|
||||
TerminalConnection::ITerminalConnection /*existingConnection*/)
|
||||
try
|
||||
{
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };
|
||||
|
||||
_CreateNewTabWithProfileAndSettings(profile, settings, existingConnection);
|
||||
// TODO! Handle defterm connections here (and above)
|
||||
// _CreateNewTabWithProfileAndSettings(profile, settings, existingConnection);
|
||||
_CreateTabWithContent(profile, settings, nullptr);
|
||||
|
||||
const uint32_t tabCount = _tabs.Size();
|
||||
const bool usedManualProfile = (newTerminalArgs != nullptr) &&
|
||||
(newTerminalArgs.ProfileIndex() != nullptr ||
|
||||
newTerminalArgs.Profile().empty());
|
||||
// const uint32_t tabCount = _tabs.Size();
|
||||
// const bool usedManualProfile = (newTerminalArgs != nullptr) &&
|
||||
// (newTerminalArgs.ProfileIndex() != nullptr ||
|
||||
// newTerminalArgs.Profile().empty());
|
||||
|
||||
// Lookup the name of the color scheme used by this profile.
|
||||
const auto scheme = _settings.GetColorSchemeForProfile(profile);
|
||||
// If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
|
||||
// that as the empty string.
|
||||
const auto schemeName = scheme ? scheme.Name() : L"\0";
|
||||
// // Lookup the name of the color scheme used by this profile.
|
||||
// const auto scheme = _settings.GetColorSchemeForProfile(profile);
|
||||
// // If they explicitly specified `null` as the scheme (indicating _no_ scheme), log
|
||||
// // that as the empty string.
|
||||
// const auto schemeName = scheme ? scheme.Name() : L"\0";
|
||||
|
||||
TraceLoggingWrite(
|
||||
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
"TabInformation",
|
||||
TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
|
||||
TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
|
||||
TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
|
||||
TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
|
||||
TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
|
||||
TraceLoggingFloat64(settings.DefaultSettings().TintOpacity(), "TintOpacity", "Opacity preference from the settings"),
|
||||
TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
|
||||
TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
|
||||
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
// TraceLoggingWrite(
|
||||
// g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
|
||||
// "TabInformation",
|
||||
// TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"),
|
||||
// TraceLoggingUInt32(1u, "EventVer", "Version of this event"),
|
||||
// TraceLoggingUInt32(tabCount, "TabCount", "Count of tabs currently opened in TerminalApp"),
|
||||
// TraceLoggingBool(usedManualProfile, "ProfileSpecified", "Whether the new tab specified a profile explicitly"),
|
||||
// TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The GUID of the profile spawned in the new tab"),
|
||||
// TraceLoggingBool(settings.DefaultSettings().UseAcrylic(), "UseAcrylic", "The acrylic preference from the settings"),
|
||||
// TraceLoggingFloat64(settings.DefaultSettings().TintOpacity(), "TintOpacity", "Opacity preference from the settings"),
|
||||
// TraceLoggingWideString(settings.DefaultSettings().FontFace().c_str(), "FontFace", "Font face chosen in the settings"),
|
||||
// TraceLoggingWideString(schemeName.data(), "SchemeName", "Color scheme set in the settings"),
|
||||
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
// TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -228,6 +231,129 @@ namespace winrt::TerminalApp::implementation
|
|||
_InitializeTab(newTabImpl);
|
||||
}
|
||||
|
||||
static wil::unique_process_information _createHostClassProcess(const winrt::guid& g)
|
||||
{
|
||||
auto guidStr{ ::Microsoft::Console::Utils::GuidToString(g) };
|
||||
|
||||
// Create an event that the content process will use to signal it is
|
||||
// ready to go. We won't need the event after this function, so the
|
||||
// unique_event will clean up our handle when we leave this scope. The
|
||||
// ContentProcess is responsible for cleaning up its own handle.
|
||||
wil::unique_event ev{ CreateEvent(nullptr, true, false, nullptr /*L"contentProcessStarted"*/) };
|
||||
// Make sure to mark this handle as inheritable! Even with
|
||||
// bInheritHandles=true, this is only inherited when it's explicitly
|
||||
// allowed to be.
|
||||
SetHandleInformation(ev.get(), HANDLE_FLAG_INHERIT, 1);
|
||||
|
||||
// god bless, fmt::format will format a HANDLE like `0xa80`
|
||||
std::wstring commandline{
|
||||
fmt::format(L"windowsterminal.exe --content {} --signal {}", guidStr, ev.get())
|
||||
};
|
||||
|
||||
STARTUPINFO siOne{ 0 };
|
||||
siOne.cb = sizeof(STARTUPINFOW);
|
||||
wil::unique_process_information piOne;
|
||||
auto succeeded = CreateProcessW(
|
||||
nullptr,
|
||||
commandline.data(),
|
||||
nullptr, // lpProcessAttributes
|
||||
nullptr, // lpThreadAttributes
|
||||
true, // bInheritHandles
|
||||
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
||||
nullptr, // lpEnvironment
|
||||
nullptr, // startingDirectory
|
||||
&siOne, // lpStartupInfo
|
||||
&piOne // lpProcessInformation
|
||||
);
|
||||
THROW_IF_WIN32_BOOL_FALSE(succeeded);
|
||||
|
||||
// Wait for the child process to signal that they're ready.
|
||||
WaitForSingleObject(ev.get(), INFINITE);
|
||||
|
||||
return std::move(piOne);
|
||||
}
|
||||
|
||||
Windows::Foundation::IAsyncOperation<ContentProcess> TerminalPage::_CreateNewContentProcess(Profile profile,
|
||||
TerminalSettingsCreateResult settings)
|
||||
{
|
||||
co_await winrt::resume_background();
|
||||
winrt::guid contentGuid{ ::Microsoft::Console::Utils::CreateGuid() };
|
||||
// Spawn a wt.exe, with the guid on the commandline
|
||||
auto piContentProcess{ _createHostClassProcess(contentGuid) };
|
||||
// THIS MUST TAKE PLACE AFTER _createHostClassProcess.
|
||||
// * If we're creating a new OOP control, _createHostClassProcess will
|
||||
// spawn the process that will actually host the ContentProcess
|
||||
// object.
|
||||
// * If we're attaching, then that process already exists.
|
||||
ContentProcess content{ nullptr };
|
||||
try
|
||||
{
|
||||
content = create_instance<ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
|
||||
}
|
||||
catch (winrt::hresult_error hr)
|
||||
{
|
||||
// _writeToLog(L"CreateInstance the ContentProces object");
|
||||
// _writeToLog(fmt::format(L" HR ({}): {}", hr.code(), hr.message().c_str()));
|
||||
co_return nullptr; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
|
||||
if (content == nullptr)
|
||||
{
|
||||
// _writeToLog(L"Failed to connect to the ContentProces object. It may not have been started fast enough.");
|
||||
co_return nullptr; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
|
||||
TerminalConnection::ConnectionInformation connectInfo{ _CreateConnectionInfoFromSettings(profile, settings.DefaultSettings()) };
|
||||
// TODO! how do we init the content proc with the focused/unfocused pair correctly?
|
||||
if (!content.Initialize(settings.DefaultSettings(), connectInfo))
|
||||
{
|
||||
// _writeToLog(L"Failed to Initialize the ContentProces object.");
|
||||
co_return nullptr; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
|
||||
co_return content;
|
||||
}
|
||||
|
||||
ContentProcess TerminalPage::_AttachToContentProcess(const winrt::guid contentGuid)
|
||||
{
|
||||
ContentProcess content{ nullptr };
|
||||
try
|
||||
{
|
||||
content = create_instance<ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
|
||||
}
|
||||
catch (winrt::hresult_error hr)
|
||||
{
|
||||
// _writeToLog(L"CreateInstance the ContentProces object");
|
||||
// _writeToLog(fmt::format(L" HR ({}): {}", hr.code(), hr.message().c_str()));
|
||||
// return nullptr; // be sure to co_return or we'll fall through to the part where we clear the log
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::_CreateTabWithContent(Profile profile,
|
||||
TerminalSettingsCreateResult settings,
|
||||
ContentProcess existingContentProc)
|
||||
{
|
||||
// Capture calling context.
|
||||
winrt::apartment_context ui_thread;
|
||||
co_await winrt::resume_background();
|
||||
auto content = existingContentProc ? existingContentProc : _CreateNewContentProcess(profile, settings).get();
|
||||
if (!content)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
// Switch back to the UI thread.
|
||||
co_await ui_thread;
|
||||
// Create the XAML control that will be attached to the content process.
|
||||
// We're not passing in a connection, because the contentGuid will be used instead.
|
||||
auto term = _InitControl(settings, content.Guid());
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(profile, term);
|
||||
_RegisterTerminalEvents(term);
|
||||
_InitializeTab(newTabImpl);
|
||||
co_return;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new tab with the given settings. If the tab bar is not being
|
||||
// currently displayed, it will be shown.
|
||||
|
@ -235,7 +361,9 @@ namespace winrt::TerminalApp::implementation
|
|||
// - profile: profile settings for this connection
|
||||
// - settings: the TerminalSettings object to use to create the TerminalControl with.
|
||||
// - existingConnection: optionally receives a connection from the outside world instead of attempting to create one
|
||||
void TerminalPage::_CreateNewTabWithProfileAndSettings(const Profile& profile, const TerminalSettingsCreateResult& settings, TerminalConnection::ITerminalConnection existingConnection)
|
||||
void TerminalPage::_CreateNewTabWithProfileAndSettings(const Profile& profile,
|
||||
const TerminalSettingsCreateResult& settings,
|
||||
TerminalConnection::ITerminalConnection existingConnection)
|
||||
{
|
||||
// Initialize the new tab
|
||||
// Create a connection based on the values in our settings object if we weren't given one.
|
||||
|
@ -360,16 +488,20 @@ namespace winrt::TerminalApp::implementation
|
|||
settingsCreateResult.DefaultSettings().StartingDirectory(workingDirectory);
|
||||
}
|
||||
|
||||
_CreateNewTabWithProfileAndSettings(profile, settingsCreateResult);
|
||||
_CreateTabWithContent(profile, settingsCreateResult, nullptr);
|
||||
|
||||
const auto runtimeTabText{ tab.GetTabText() };
|
||||
if (!runtimeTabText.empty())
|
||||
{
|
||||
if (auto newTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
newTab->SetTabText(runtimeTabText);
|
||||
}
|
||||
}
|
||||
// TODO! duplicating a tab should dup the tab title over, but
|
||||
// _CreateTabWithContent will return before the tab actually
|
||||
// exists.
|
||||
//
|
||||
// const auto runtimeTabText{ tab.GetTabText() };
|
||||
// if (!runtimeTabText.empty())
|
||||
// {
|
||||
// if (auto newTab{ _GetFocusedTabImpl() })
|
||||
// {
|
||||
// newTab->SetTabText(runtimeTabText);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "DebugTapConnection.h"
|
||||
#include "SettingsTab.h"
|
||||
#include "RenameWindowRequestedArgs.g.cpp"
|
||||
#include "RequestMovePaneArgs.g.cpp"
|
||||
#include "../inc/WindowingBehavior.h"
|
||||
|
||||
#include <til/latch.h>
|
||||
|
@ -461,7 +462,12 @@ namespace winrt::TerminalApp::implementation
|
|||
// GH#6586: now that we're done processing all startup commands,
|
||||
// focus the active control. This will work as expected for both
|
||||
// commandline invocations and for `wt` action invocations.
|
||||
_GetActiveControl().Focus(FocusState::Programmatic);
|
||||
if (auto activeControl{ _GetActiveControl() })
|
||||
{
|
||||
// TODO! this doesn't work during startup anymore now that
|
||||
// tabs are made on a BG thread
|
||||
activeControl.Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
if (initial)
|
||||
{
|
||||
|
@ -898,6 +904,103 @@ namespace winrt::TerminalApp::implementation
|
|||
return connection;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Creates a new connection based on the profile settings
|
||||
// Arguments:
|
||||
// - the profile we want the settings from
|
||||
// - the terminal settings
|
||||
// Return value:
|
||||
// - the desired connection
|
||||
TerminalConnection::ConnectionInformation TerminalPage::_CreateConnectionInfoFromSettings(Profile profile,
|
||||
const TerminalSettings& settings)
|
||||
{
|
||||
if (!profile)
|
||||
{
|
||||
// Use the default profile if we didn't get one as an argument.
|
||||
profile = _settings.FindProfile(_settings.GlobalSettings().DefaultProfile());
|
||||
}
|
||||
|
||||
winrt::guid connectionType = profile.ConnectionType();
|
||||
winrt::guid sessionGuid{};
|
||||
|
||||
Windows::Foundation::Collections::ValueSet connectionSettings{ nullptr };
|
||||
winrt::hstring className;
|
||||
|
||||
if (connectionType == TerminalConnection::AzureConnection::ConnectionType() &&
|
||||
TerminalConnection::AzureConnection::IsAzureConnectionAvailable())
|
||||
{
|
||||
// TODO GH#4661: Replace this with directly using the AzCon when our VT is better
|
||||
std::filesystem::path azBridgePath{ wil::GetModuleFileNameW<std::wstring>(nullptr) };
|
||||
azBridgePath.replace_filename(L"TerminalAzBridge.exe");
|
||||
className = winrt::name_of<TerminalConnection::ConptyConnection>();
|
||||
connectionSettings = TerminalConnection::ConptyConnection::CreateSettings(azBridgePath.wstring(),
|
||||
L".",
|
||||
L"Azure",
|
||||
nullptr,
|
||||
::base::saturated_cast<uint32_t>(settings.InitialRows()),
|
||||
::base::saturated_cast<uint32_t>(settings.InitialCols()),
|
||||
winrt::guid());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// profile is guaranteed to exist here
|
||||
std::wstring guidWString = Utils::GuidToString(profile.Guid());
|
||||
|
||||
StringMap envMap{};
|
||||
envMap.Insert(L"WT_PROFILE_ID", guidWString);
|
||||
envMap.Insert(L"WSLENV", L"WT_PROFILE_ID");
|
||||
|
||||
// Update the path to be relative to whatever our CWD is.
|
||||
//
|
||||
// Refer to the examples in
|
||||
// https://en.cppreference.com/w/cpp/filesystem/path/append
|
||||
//
|
||||
// We need to do this here, to ensure we tell the ConptyConnection
|
||||
// the correct starting path. If we're being invoked from another
|
||||
// terminal instance (e.g. wt -w 0 -d .), then we have switched our
|
||||
// CWD to the provided path. We should treat the StartingDirectory
|
||||
// as relative to the current CWD.
|
||||
//
|
||||
// The connection must be informed of the current CWD on
|
||||
// construction, because the connection might not spawn the child
|
||||
// process until later, on another thread, after we've already
|
||||
// restored the CWD to it's original value.
|
||||
winrt::hstring newWorkingDirectory{ settings.StartingDirectory() };
|
||||
if (newWorkingDirectory.size() <= 1 ||
|
||||
!(newWorkingDirectory[0] == L'~' || newWorkingDirectory[0] == L'/'))
|
||||
{ // We only want to resolve the new WD against the CWD if it doesn't look like a Linux path (see GH#592)
|
||||
std::wstring cwdString{ wil::GetCurrentDirectoryW<std::wstring>() };
|
||||
std::filesystem::path cwd{ cwdString };
|
||||
cwd /= settings.StartingDirectory().c_str();
|
||||
newWorkingDirectory = winrt::hstring{ cwd.wstring() };
|
||||
}
|
||||
|
||||
className = winrt::name_of<TerminalConnection::ConptyConnection>();
|
||||
connectionSettings = TerminalConnection::ConptyConnection::CreateSettings(settings.Commandline(),
|
||||
newWorkingDirectory,
|
||||
settings.StartingTitle(),
|
||||
envMap.GetView(),
|
||||
::base::saturated_cast<uint32_t>(settings.InitialRows()),
|
||||
::base::saturated_cast<uint32_t>(settings.InitialCols()),
|
||||
winrt::guid());
|
||||
|
||||
// sessionGuid = conhostConn.Guid();
|
||||
}
|
||||
|
||||
// TraceLoggingWrite(
|
||||
// g_hTerminalAppProvider,
|
||||
// "ConnectionCreated",
|
||||
// TraceLoggingDescription("Event emitted upon the creation of a connection"),
|
||||
// TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"),
|
||||
// TraceLoggingGuid(profile.Guid(), "ProfileGuid", "The profile's GUID"),
|
||||
// TraceLoggingGuid(sessionGuid, "SessionGuid", "The WT_SESSION's GUID"),
|
||||
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||
// TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
|
||||
|
||||
return TerminalConnection::ConnectionInformation(className, connectionSettings);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the settings button is clicked. Launches a background
|
||||
// thread to open the settings file in the default JSON editor.
|
||||
|
@ -1257,11 +1360,14 @@ namespace winrt::TerminalApp::implementation
|
|||
// - No move will occur if the tabIdx is the same as the current tab, or if
|
||||
// the specified tab is not a host of terminals (such as the settings tab).
|
||||
// Arguments:
|
||||
// - tabIdx: The target tab index.
|
||||
// - TODO!
|
||||
// Return Value:
|
||||
// - true if the pane was successfully moved to the new tab.
|
||||
bool TerminalPage::_MovePane(const uint32_t tabIdx)
|
||||
bool TerminalPage::_MovePane(MovePaneArgs args)
|
||||
{
|
||||
const auto tabIdx{ args.TabIndex() };
|
||||
const auto windowId{ args.Window() };
|
||||
|
||||
auto focusedTab{ _GetFocusedTabImpl() };
|
||||
|
||||
if (!focusedTab)
|
||||
|
@ -1269,6 +1375,17 @@ namespace winrt::TerminalApp::implementation
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!windowId.empty())
|
||||
{
|
||||
if (const auto& control{ _GetActiveControl() })
|
||||
{
|
||||
const auto currentContentGuid{ control.ContentGuid() };
|
||||
auto request = winrt::make_self<RequestMovePaneArgs>(currentContentGuid, args);
|
||||
_RequestMovePaneHandlers(*this, *request);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are trying to move from the current tab to the current tab do nothing.
|
||||
if (_GetFocusedTabIndex() == tabIdx)
|
||||
{
|
||||
|
@ -1299,6 +1416,45 @@ namespace winrt::TerminalApp::implementation
|
|||
return true;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::AttachPane(winrt::guid contentGuid, uint32_t tabIndex)
|
||||
{
|
||||
contentGuid;
|
||||
tabIndex;
|
||||
co_await winrt::resume_background();
|
||||
const auto contentProc = _AttachToContentProcess(contentGuid);
|
||||
contentProc;
|
||||
|
||||
Settings::Model::NewTerminalArgs newTerminalArgs{ nullptr };
|
||||
auto profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
auto controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
|
||||
|
||||
co_await winrt::resume_foreground(Dispatcher());
|
||||
|
||||
auto newControl = _InitControl(controlSettings, contentProc.Guid());
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(newControl);
|
||||
_UnZoomIfNeeded();
|
||||
|
||||
if (_tabs.Size() > tabIndex)
|
||||
{
|
||||
auto targetTab = _GetTerminalTabImpl(_tabs.GetAt(tabIndex));
|
||||
targetTab->SplitPane(SplitState::Vertical, .5f, profile, newControl);
|
||||
|
||||
// After GH#6586, the control will no longer focus itself
|
||||
// automatically when it's finished being laid out. Manually focus
|
||||
// the control here instead.
|
||||
if (_startupState == StartupState::Initialized)
|
||||
{
|
||||
_GetActiveControl().Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// realSplitType = tab.PreCalculateAutoSplit(availableSpace);
|
||||
|
||||
// tab.SplitPane(realSplitType, splitSize, profile, newControl);
|
||||
// }
|
||||
}
|
||||
// Method Description:
|
||||
// - Split the focused pane either horizontally or vertically, and place the
|
||||
// given TermControl into the newly created pane.
|
||||
|
@ -1344,11 +1500,11 @@ namespace winrt::TerminalApp::implementation
|
|||
// - newTerminalArgs: An object that may contain a blob of parameters to
|
||||
// control which profile is created and with possible other
|
||||
// configurations. See CascadiaSettings::BuildSettings for more details.
|
||||
void TerminalPage::_SplitPane(TerminalTab& tab,
|
||||
const SplitState splitType,
|
||||
const SplitType splitMode,
|
||||
const float splitSize,
|
||||
const NewTerminalArgs& newTerminalArgs)
|
||||
winrt::fire_and_forget TerminalPage::_SplitPane(TerminalTab& tab,
|
||||
const SplitState splitType,
|
||||
const SplitType splitMode,
|
||||
const float splitSize,
|
||||
const NewTerminalArgs& newTerminalArgs)
|
||||
{
|
||||
// Do nothing if we're requesting no split.
|
||||
if (splitType == SplitState::None)
|
||||
|
@ -1393,8 +1549,6 @@ namespace winrt::TerminalApp::implementation
|
|||
controlSettings = TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings);
|
||||
}
|
||||
|
||||
const auto controlConnection = _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
|
||||
|
||||
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
|
||||
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
|
||||
const winrt::Windows::Foundation::Size availableSpace{ contentWidth, contentHeight };
|
||||
|
@ -1408,10 +1562,13 @@ namespace winrt::TerminalApp::implementation
|
|||
const auto canSplit = tab.PreCalculateCanSplit(realSplitType, splitSize, availableSpace);
|
||||
if (!canSplit)
|
||||
{
|
||||
return;
|
||||
co_return;
|
||||
}
|
||||
|
||||
auto newControl = _InitControl(controlSettings, controlConnection);
|
||||
co_await winrt::resume_background();
|
||||
const auto contentProc = _CreateNewContentProcess(profile, controlSettings).get();
|
||||
co_await winrt::resume_foreground(Dispatcher());
|
||||
auto newControl = _InitControl(controlSettings, contentProc.Guid());
|
||||
|
||||
// Hookup our event handlers to the new terminal
|
||||
_RegisterTerminalEvents(newControl);
|
||||
|
@ -1996,6 +2153,17 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
return term;
|
||||
}
|
||||
TermControl TerminalPage::_InitControl(const TerminalSettingsCreateResult& settings, const winrt::guid& contentGuid)
|
||||
{
|
||||
// Give term control a child of the settings so that any overrides go in the child
|
||||
// This way, when we do a settings reload we just update the parent and the overrides remain
|
||||
const auto child = TerminalSettings::CreateWithParent(settings);
|
||||
TermControl term{ contentGuid, child.DefaultSettings(), nullptr };
|
||||
|
||||
term.UnfocusedAppearance(child.UnfocusedSettings()); // It is okay for the unfocused settings to be null
|
||||
|
||||
return term;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Hook up keybindings, and refresh the UI of the terminal.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "AppKeyBindings.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
#include "RenameWindowRequestedArgs.g.h"
|
||||
#include "RequestMovePaneArgs.g.h"
|
||||
#include "Toast.h"
|
||||
|
||||
#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
|
||||
|
@ -45,6 +46,16 @@ namespace winrt::TerminalApp::implementation
|
|||
_ProposedName{ name } {};
|
||||
};
|
||||
|
||||
struct RequestMovePaneArgs : RequestMovePaneArgsT<RequestMovePaneArgs>
|
||||
{
|
||||
WINRT_PROPERTY(winrt::guid, ContentGuid);
|
||||
WINRT_PROPERTY(Microsoft::Terminal::Settings::Model::MovePaneArgs, Args, nullptr);
|
||||
|
||||
public:
|
||||
RequestMovePaneArgs(const winrt::guid& g, Microsoft::Terminal::Settings::Model::MovePaneArgs args) :
|
||||
_ContentGuid{ g }, _Args{ args } {};
|
||||
};
|
||||
|
||||
struct TerminalPage : TerminalPageT<TerminalPage>
|
||||
{
|
||||
public:
|
||||
|
@ -107,6 +118,7 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::hstring WindowIdForDisplay() const noexcept;
|
||||
winrt::hstring WindowNameForDisplay() const noexcept;
|
||||
bool IsQuakeWindow() const noexcept;
|
||||
winrt::fire_and_forget AttachPane(winrt::guid contentGuid, uint32_t tabIndex);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
|
@ -124,6 +136,7 @@ namespace winrt::TerminalApp::implementation
|
|||
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
|
||||
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
|
||||
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
|
||||
TYPED_EVENT(RequestMovePane, Windows::Foundation::IInspectable, winrt::TerminalApp::RequestMovePaneArgs);
|
||||
|
||||
private:
|
||||
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
|
||||
|
@ -190,7 +203,13 @@ namespace winrt::TerminalApp::implementation
|
|||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane);
|
||||
void _CreateNewTabWithProfileAndSettings(const Microsoft::Terminal::Settings::Model::Profile& profile, const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
winrt::Windows::Foundation::IAsyncOperation<Microsoft::Terminal::Control::ContentProcess> _CreateNewContentProcess(Microsoft::Terminal::Settings::Model::Profile profile,
|
||||
Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult settings);
|
||||
Microsoft::Terminal::Control::ContentProcess _AttachToContentProcess(const winrt::guid contentGuid);
|
||||
|
||||
winrt::fire_and_forget _CreateTabWithContent(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult settings, Microsoft::Terminal::Control::ContentProcess existingContentProc);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ConnectionInformation _CreateConnectionInfoFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, const Microsoft::Terminal::Settings::Model::TerminalSettings& settings);
|
||||
|
||||
winrt::fire_and_forget _OpenNewWindow(const bool elevate, const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
|
@ -240,7 +259,7 @@ namespace winrt::TerminalApp::implementation
|
|||
bool _SelectTab(uint32_t tabIndex);
|
||||
bool _MoveFocus(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _SwapPane(const Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool _MovePane(const uint32_t tabIdx);
|
||||
bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl();
|
||||
std::optional<uint32_t> _GetFocusedTabIndex() const noexcept;
|
||||
|
@ -259,11 +278,11 @@ namespace winrt::TerminalApp::implementation
|
|||
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
|
||||
const float splitSize = 0.5f,
|
||||
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
void _SplitPane(TerminalTab& tab,
|
||||
const Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
|
||||
const float splitSize = 0.5f,
|
||||
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
winrt::fire_and_forget _SplitPane(TerminalTab& tab,
|
||||
const Microsoft::Terminal::Settings::Model::SplitState splitType,
|
||||
const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual,
|
||||
const float splitSize = 0.5f,
|
||||
const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr);
|
||||
void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
void _ToggleSplitOrientation();
|
||||
|
||||
|
@ -310,6 +329,9 @@ namespace winrt::TerminalApp::implementation
|
|||
winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection);
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const winrt::guid& contentGuid);
|
||||
|
||||
void _RefreshUIForSettingsReload();
|
||||
|
||||
void _SetNonClientAreaColors(const Windows::UI::Color& selectedTabColor);
|
||||
|
|
|
@ -10,6 +10,11 @@ namespace TerminalApp
|
|||
{
|
||||
String ProposedName { get; };
|
||||
};
|
||||
[default_interface] runtimeclass RequestMovePaneArgs
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.MovePaneArgs Args { get; };
|
||||
Guid ContentGuid { get; };
|
||||
};
|
||||
|
||||
interface IDialogPresenter
|
||||
{
|
||||
|
@ -44,6 +49,7 @@ namespace TerminalApp
|
|||
String KeyboardServiceDisabledText { get; };
|
||||
|
||||
TaskbarState TaskbarState{ get; };
|
||||
void AttachPane(Guid contentGuid, UInt32 tabIndex);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, String> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, LastTabClosedEventArgs> LastTabClosed;
|
||||
|
@ -57,5 +63,6 @@ namespace TerminalApp
|
|||
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, RequestMovePaneArgs> RequestMovePane;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,21 +36,59 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
::IInspectable** raw = reinterpret_cast<::IInspectable**>(pointer);
|
||||
#pragma warning(pop)
|
||||
|
||||
// RoActivateInstance() will try to create an instance of the object,
|
||||
// who's fully qualified name is the string in Name().
|
||||
TerminalConnection::ITerminalConnection connection{ nullptr };
|
||||
|
||||
// A couple short-circuits, for connections that _we_ implement.
|
||||
// Sometimes, RoActivateInstance is weird and fails with errors like the
|
||||
// following
|
||||
//
|
||||
// The class has to be activatable. For the Terminal, this is easy
|
||||
// enough - we're not hosting anything that's not already in our
|
||||
// manifest, or living as a .dll & .winmd SxS.
|
||||
//
|
||||
// When we get to extensions (GH#4000), we may want to revisit.
|
||||
if (LOG_IF_FAILED(RoActivateInstance(name, raw)))
|
||||
/*
|
||||
onecore\com\combase\inc\RegistryKey.hpp(527)\combase.dll!00007FFF75E1F855:
|
||||
(caller: 00007FFF75D3BC29) LogHr(2) tid(83a8) 800700A1 The specified
|
||||
path is invalid.
|
||||
Msg:[StaticNtOpen failed with
|
||||
path:\REGISTRY\A\{A41685A4-AD85-4C4C-BA5D-A849ADBF3C40}\ActivatableClassId
|
||||
\REGISTRY\MACHINE\Software\Classes\ActivatableClasses]
|
||||
...\src\cascadia\TerminalConnection\ConnectionInformation.cpp(47)\TerminalConnection.dll!00007FFEC1381FC5:
|
||||
(caller: 00007FFEC13810A5) LogHr(1) tid(83a8) 800700A1 The specified
|
||||
path is invalid.
|
||||
[...TerminalConnection::implementation::ConnectionInformation::CreateConnection(RoActivateInstance(name,
|
||||
raw))]
|
||||
*/
|
||||
//
|
||||
// So to avoid those, we'll manually instantiate these
|
||||
if (info.ClassName() == winrt::name_of<TerminalConnection::ConptyConnection>())
|
||||
{
|
||||
return nullptr;
|
||||
connection = TerminalConnection::ConptyConnection();
|
||||
}
|
||||
else if (info.ClassName() == winrt::name_of<TerminalConnection::AzureConnection>())
|
||||
{
|
||||
connection = TerminalConnection::AzureConnection();
|
||||
}
|
||||
else if (info.ClassName() == winrt::name_of<TerminalConnection::EchoConnection>())
|
||||
{
|
||||
connection = TerminalConnection::EchoConnection();
|
||||
}
|
||||
else
|
||||
{
|
||||
// RoActivateInstance() will try to create an instance of the object,
|
||||
// who's fully qualified name is the string in Name().
|
||||
//
|
||||
// The class has to be activatable. For the Terminal, this is easy
|
||||
// enough - we're not hosting anything that's not already in our
|
||||
// manifest, or living as a .dll & .winmd SxS.
|
||||
//
|
||||
// When we get to extensions (GH#4000), we may want to revisit.
|
||||
if (LOG_IF_FAILED(RoActivateInstance(name, raw)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
connection = inspectable.try_as<TerminalConnection::ITerminalConnection>();
|
||||
}
|
||||
|
||||
// Now that thing we made, make sure it's actually a ITerminalConnection
|
||||
if (const auto connection{ inspectable.try_as<TerminalConnection::ITerminalConnection>() })
|
||||
if (connection)
|
||||
{
|
||||
// Initialize it, and return it.
|
||||
connection.Initialize(info.Settings());
|
||||
|
|
105
src/cascadia/TerminalControl/ContentProcess.cpp
Normal file
105
src/cascadia/TerminalControl/ContentProcess.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ContentProcess.h"
|
||||
#include "ContentProcess.g.cpp"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
ContentProcess::ContentProcess(winrt::guid g) :
|
||||
_ourPID{ GetCurrentProcessId() }, _guid{ g } {}
|
||||
|
||||
bool ContentProcess::Initialize(Control::IControlSettings settings,
|
||||
TerminalConnection::ConnectionInformation connectionInfo)
|
||||
{
|
||||
auto conn{ TerminalConnection::ConnectionInformation::CreateConnection(connectionInfo) };
|
||||
if (conn == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, conn);
|
||||
return true;
|
||||
}
|
||||
|
||||
ContentProcess::~ContentProcess()
|
||||
{
|
||||
// DANGER - We're straight up going to EXIT THE ENTIRE PROCESS when we
|
||||
// get destructed. This eliminates the need to do any sort of
|
||||
// refcounting weirdness. This entire process exists to host one
|
||||
// singular ContentProcess instance. When we're destructed, it's because
|
||||
// every other window process was done with us. We can die now, knowing
|
||||
// that our job is complete.
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
Control::ControlInteractivity ContentProcess::GetInteractivity()
|
||||
{
|
||||
return _interactivity;
|
||||
}
|
||||
|
||||
uint64_t ContentProcess::GetPID()
|
||||
{
|
||||
return _ourPID;
|
||||
}
|
||||
winrt::guid ContentProcess::Guid()
|
||||
{
|
||||
return _guid;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Duplicate the swap chain handle to the provided process.
|
||||
// - If the provided PID is our pid, then great - we don't need to do anything.
|
||||
// Arguments:
|
||||
// - callersPid: the PID of the process calling this method.
|
||||
// Return Value:
|
||||
// - The value of the swapchain handle in the callers process
|
||||
// Notes:
|
||||
// - This is BODGY! We're basically asking to marshal a HANDLE here. WinRT
|
||||
// has no good mechanism for doing this, so we're doing it by casting the
|
||||
// value to a uint64_t. In all reality, we _should_ be using a COM
|
||||
// interface for this, because it can set up the security on these handles
|
||||
// more appropriately. Fortunately, all we're dealing with is swapchains,
|
||||
// so the security doesn't matter all that much.
|
||||
uint64_t ContentProcess::RequestSwapChainHandle(const uint64_t callersPid)
|
||||
{
|
||||
auto ourPid = GetCurrentProcessId();
|
||||
HANDLE ourHandle = reinterpret_cast<HANDLE>(_interactivity.Core().SwapChainHandle());
|
||||
if (callersPid == ourPid)
|
||||
{
|
||||
return reinterpret_cast<uint64_t>(ourHandle);
|
||||
}
|
||||
|
||||
wil::unique_handle hWindowProcess{ OpenProcess(PROCESS_ALL_ACCESS,
|
||||
FALSE,
|
||||
static_cast<DWORD>(callersPid)) };
|
||||
if (hWindowProcess.get() == nullptr)
|
||||
{
|
||||
const auto gle = GetLastError();
|
||||
gle;
|
||||
// TODO! tracelog an error here
|
||||
return 0;
|
||||
}
|
||||
|
||||
HANDLE theirHandle{ nullptr };
|
||||
BOOL success = DuplicateHandle(GetCurrentProcess(),
|
||||
ourHandle,
|
||||
hWindowProcess.get(),
|
||||
&theirHandle,
|
||||
0,
|
||||
FALSE,
|
||||
DUPLICATE_SAME_ACCESS);
|
||||
if (!success)
|
||||
{
|
||||
const auto gle = GetLastError();
|
||||
gle;
|
||||
// TODO! tracelog an error here
|
||||
return 0;
|
||||
}
|
||||
|
||||
// At this point, the handle is now in their process space, with value
|
||||
// theirHandle
|
||||
return reinterpret_cast<uint64_t>(theirHandle);
|
||||
}
|
||||
|
||||
}
|
33
src/cascadia/TerminalControl/ContentProcess.h
Normal file
33
src/cascadia/TerminalControl/ContentProcess.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ContentProcess.g.h"
|
||||
#include "ControlInteractivity.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
struct ContentProcess : ContentProcessT<ContentProcess>
|
||||
{
|
||||
ContentProcess(winrt::guid g);
|
||||
~ContentProcess();
|
||||
bool Initialize(Control::IControlSettings settings,
|
||||
TerminalConnection::ConnectionInformation connectionInfo);
|
||||
Control::ControlInteractivity GetInteractivity();
|
||||
|
||||
uint64_t GetPID();
|
||||
winrt::guid Guid();
|
||||
uint64_t RequestSwapChainHandle(const uint64_t pid);
|
||||
|
||||
private:
|
||||
Control::ControlInteractivity _interactivity{ nullptr };
|
||||
uint64_t _ourPID;
|
||||
winrt::guid _guid;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ContentProcess);
|
||||
}
|
22
src/cascadia/TerminalControl/ContentProcess.idl
Normal file
22
src/cascadia/TerminalControl/ContentProcess.idl
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "ControlInteractivity.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
runtimeclass ContentProcess {
|
||||
|
||||
ContentProcess(Guid g);
|
||||
|
||||
Boolean Initialize(IControlSettings settings,
|
||||
Microsoft.Terminal.TerminalConnection.ConnectionInformation connectionInfo);
|
||||
|
||||
ControlInteractivity GetInteractivity();
|
||||
|
||||
UInt64 GetPID();
|
||||
Guid Guid { get; };
|
||||
|
||||
UInt64 RequestSwapChainHandle(UInt64 pid);
|
||||
};
|
||||
}
|
|
@ -595,7 +595,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_desiredFont = { _actualFont };
|
||||
|
||||
// Update the terminal core with its new Core settings
|
||||
_terminal->UpdateSettings(_settings);
|
||||
// Are you seeing a crash specifically on this line?
|
||||
Core::ICoreSettings coreSettings{ _settings };
|
||||
// Then you might not have Microsoft.Terminal.Core.winmd in the package
|
||||
// or SxS with windowsterminal.exe!
|
||||
_terminal->UpdateSettings(coreSettings);
|
||||
|
||||
if (!_initializedTerminal)
|
||||
{
|
||||
|
|
|
@ -247,14 +247,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (_dispatcher)
|
||||
{
|
||||
// _closing isn't atomic and may only be accessed from the main thread.
|
||||
//
|
||||
// Though, the unit tests don't actually run in TAEF's main
|
||||
// thread, so we don't care when we're running in tests.
|
||||
assert(_inUnitTests || _dispatcher.HasThreadAccess());
|
||||
}
|
||||
// // TODO! This may not be strictly true if the core is running out of
|
||||
// // proc with XAML. I keep hitting this assertion every time it
|
||||
// // exits, so we might need a better solution.
|
||||
// if (_dispatcher)
|
||||
// {
|
||||
// // _closing isn't atomic and may only be accessed from the main thread.
|
||||
// //
|
||||
// // Though, the unit tests don't actually run in TAEF's main
|
||||
// // thread, so we don't care when we're running in tests.
|
||||
// assert(_inUnitTests || _dispatcher.HasThreadAccess());
|
||||
// }
|
||||
#endif
|
||||
return _closing;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_controlPadding = padding;
|
||||
}
|
||||
|
||||
void InteractivityAutomationPeer::SetParentProvider(Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider)
|
||||
{
|
||||
_parentProvider = parentProvider;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Signals the ui automation client that the terminal's selection has
|
||||
// changed and should be updated
|
||||
|
@ -111,7 +116,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
THROW_IF_FAILED(_uiaProvider->RangeFromChild(/* IRawElementProviderSimple */ nullptr,
|
||||
&returnVal));
|
||||
|
||||
const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
// const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
const auto parentProvider = _parentProvider;
|
||||
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
}
|
||||
|
@ -121,7 +127,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
UIA::ITextRangeProvider* returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal));
|
||||
|
||||
const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
// const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
const auto parentProvider = _parentProvider;
|
||||
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
}
|
||||
|
@ -131,7 +138,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
UIA::ITextRangeProvider* returnVal;
|
||||
THROW_IF_FAILED(_uiaProvider->get_DocumentRange(&returnVal));
|
||||
|
||||
const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
// const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
const auto parentProvider = _parentProvider;
|
||||
const auto xutr = winrt::make_self<XamlUiaTextRange>(returnVal, parentProvider);
|
||||
return xutr.as<XamlAutomation::ITextRangeProvider>();
|
||||
}
|
||||
|
@ -194,7 +202,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
std::vector<XamlAutomation::ITextRangeProvider> vec;
|
||||
vec.reserve(count);
|
||||
auto parentProvider = this->ProviderFromPeer(*this);
|
||||
// auto parentProvider = this->ProviderFromPeer(*this);
|
||||
const auto parentProvider = _parentProvider;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
auto xutr = make_self<XamlUiaTextRange>(providers[i].detach(), parentProvider);
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
void SetControlBounds(const Windows::Foundation::Rect bounds);
|
||||
void SetControlPadding(const Core::Padding padding);
|
||||
void SetParentProvider(Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider);
|
||||
|
||||
#pragma region IUiaEventDispatcher
|
||||
void SignalSelectionChanged() override;
|
||||
|
@ -76,6 +77,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
private:
|
||||
::Microsoft::WRL::ComPtr<::Microsoft::Terminal::TermControlUiaProvider> _uiaProvider;
|
||||
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
|
||||
winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider{ nullptr };
|
||||
|
||||
til::rectangle _controlBounds{};
|
||||
til::rectangle _controlPadding{};
|
||||
|
|
|
@ -3,13 +3,21 @@
|
|||
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
[default_interface] runtimeclass InteractivityAutomationPeer :
|
||||
[default_interface] runtimeclass InteractivityAutomationPeer/* :
|
||||
Windows.UI.Xaml.Automation.Peers.AutomationPeer,
|
||||
Windows.UI.Xaml.Automation.Provider.ITextProvider
|
||||
Windows.UI.Xaml.Automation.Provider.ITextProvider*/
|
||||
{
|
||||
|
||||
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider[] GetSelection();
|
||||
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider[] GetVisibleRanges();
|
||||
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider RangeFromChild(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple childElement);
|
||||
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider RangeFromPoint(Windows.Foundation.Point screenLocation);
|
||||
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider DocumentRange();
|
||||
Windows.UI.Xaml.Automation.SupportedTextSelection SupportedTextSelection();
|
||||
|
||||
void SetControlBounds(Windows.Foundation.Rect bounds);
|
||||
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
|
||||
void SetParentProvider(Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple provider);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> SelectionChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> TextChanged;
|
||||
|
|
|
@ -200,4 +200,10 @@ Please either install the missing font or choose another one.</value>
|
|||
<data name="TermControlReadOnly" xml:space="preserve">
|
||||
<value>Read-only mode is enabled.</value>
|
||||
</data>
|
||||
<data name="TermControl_ContentDiedTextBlock.Text" xml:space="preserve">
|
||||
<value>The content of this terminal was closed unexpectedly.</value>
|
||||
</data>
|
||||
<data name="TermControl_ContentDiedButton.Content" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// The functions for handling content processes in the TermControl are largely
|
||||
// in TermControlContentManageent.cpp
|
||||
|
||||
#include "pch.h"
|
||||
#include "TermControl.h"
|
||||
|
@ -32,8 +35,10 @@ using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
|||
// The updates are throttled to limit power usage.
|
||||
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the TSF input control.
|
||||
// This is already throttled primarily in the ControlCore, with a timeout of 100ms. We're adding another smaller one here, as the (potentially x-proc) call will come in off the UI thread
|
||||
// The minimum delay between updating the TSF input control. This is already
|
||||
// throttled primarily in the ControlCore, with a timeout of 100ms. We're adding
|
||||
// another smaller one here, as the (potentially x-proc) call will come in off
|
||||
// the UI thread
|
||||
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(8);
|
||||
|
||||
// The minimum delay between updating the locations of regex patterns
|
||||
|
@ -50,7 +55,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
TermControl::TermControl(IControlSettings settings,
|
||||
TerminalConnection::ITerminalConnection connection) :
|
||||
TermControl(winrt::guid{}, settings, connection) {}
|
||||
|
||||
TermControl::TermControl(winrt::guid contentGuid,
|
||||
IControlSettings settings,
|
||||
TerminalConnection::ITerminalConnection connection) :
|
||||
_initializedTerminal{ false },
|
||||
_settings{ settings },
|
||||
_closing{ false },
|
||||
_isInternalScrollBarUpdate{ false },
|
||||
_autoScrollVelocity{ 0 },
|
||||
_autoScrollingPointerPoint{ std::nullopt },
|
||||
|
@ -62,7 +74,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, connection);
|
||||
if (contentGuid != winrt::guid{})
|
||||
{
|
||||
_contentProc = create_instance<Control::ContentProcess>(contentGuid, CLSCTX_LOCAL_SERVER);
|
||||
if (_contentProc != nullptr)
|
||||
{
|
||||
_interactivity = _contentProc.GetInteractivity();
|
||||
_contentWaitInterrupt.create();
|
||||
_createContentWaitThread();
|
||||
}
|
||||
}
|
||||
|
||||
if (_interactivity == nullptr)
|
||||
{
|
||||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, connection);
|
||||
}
|
||||
_core = _interactivity.Core();
|
||||
|
||||
// These events might all be triggered by the connection, but that
|
||||
|
@ -75,6 +101,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
// This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here.
|
||||
_core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState });
|
||||
|
||||
_core.ConnectionStateChanged({ get_weak(), &TermControl::_coreConnectionStateChanged });
|
||||
|
||||
// These callbacks can only really be triggered by UI interactions. So
|
||||
// they don't need weak refs - they can't be triggered unless we're
|
||||
// alive.
|
||||
|
@ -500,6 +528,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
TermControl::~TermControl()
|
||||
{
|
||||
if (_contentIsOutOfProc())
|
||||
{
|
||||
_contentWaitInterrupt.SetEvent();
|
||||
_contentWaitThread.join();
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
|
@ -526,6 +559,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
margins.Right,
|
||||
margins.Bottom };
|
||||
_automationPeer = winrt::make<implementation::TermControlAutomationPeer>(this, padding, interactivityAutoPeer);
|
||||
interactivityAutoPeer.SetParentProvider(_automationPeer.GetParentProvider());
|
||||
return _automationPeer;
|
||||
}
|
||||
}
|
||||
|
@ -546,7 +580,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
TerminalConnection::ConnectionState TermControl::ConnectionState() const
|
||||
{
|
||||
return _core.ConnectionState();
|
||||
try
|
||||
{
|
||||
return _core.ConnectionState();
|
||||
}
|
||||
CATCH_LOG();
|
||||
return TerminalConnection::ConnectionState::Closed;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::RenderEngineSwapChainChanged(IInspectable /*sender*/, IInspectable /*args*/)
|
||||
|
@ -560,7 +599,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
const HANDLE chainHandle = reinterpret_cast<HANDLE>(control->_core.SwapChainHandle());
|
||||
// TODO! very good chance we leak this handle
|
||||
const HANDLE chainHandle = reinterpret_cast<HANDLE>(control->_contentIsOutOfProc() ?
|
||||
control->_contentProc.RequestSwapChainHandle(GetCurrentProcessId()) :
|
||||
control->_core.SwapChainHandle());
|
||||
_AttachDxgiSwapChainToXaml(chainHandle);
|
||||
}
|
||||
}
|
||||
|
@ -648,7 +690,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
}
|
||||
_interactivity.Initialize();
|
||||
|
||||
_AttachDxgiSwapChainToXaml(reinterpret_cast<HANDLE>(_core.SwapChainHandle()));
|
||||
// TODO! very good chance we leak this handle
|
||||
const HANDLE chainHandle = reinterpret_cast<HANDLE>(_contentIsOutOfProc() ?
|
||||
_contentProc.RequestSwapChainHandle(GetCurrentProcessId()) :
|
||||
_core.SwapChainHandle());
|
||||
_AttachDxgiSwapChainToXaml(chainHandle);
|
||||
|
||||
// Tell the DX Engine to notify us when the swap chain changes. We do
|
||||
// this after we initially set the swapchain so as to avoid unnecessary
|
||||
|
@ -1734,8 +1780,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
// Disconnect the TSF input control so it doesn't receive EditContext events.
|
||||
TSFInputControl().Close();
|
||||
_autoScrollTimer.Stop();
|
||||
|
||||
_core.Close();
|
||||
if (!_contentIsOutOfProc())
|
||||
{
|
||||
_core.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2568,4 +2616,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
_playWarningBell->Run();
|
||||
}
|
||||
void TermControl::_coreConnectionStateChanged(const IInspectable& /*sender*/, const IInspectable& /*args*/)
|
||||
{
|
||||
_ConnectionStateChangedHandlers(*this, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
struct TermControl : TermControlT<TermControl>
|
||||
{
|
||||
TermControl(winrt::guid contentGuid, IControlSettings settings, TerminalConnection::ITerminalConnection connection);
|
||||
TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection);
|
||||
|
||||
winrt::guid ContentGuid() const;
|
||||
|
||||
winrt::fire_and_forget UpdateSettings();
|
||||
winrt::fire_and_forget UpdateAppearance(const IControlAppearance newAppearance);
|
||||
|
||||
|
@ -70,6 +73,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args);
|
||||
|
||||
void _RenderRetryButton_Click(IInspectable const& button, IInspectable const& args);
|
||||
void _ContentDiedCloseButton_Click(IInspectable const& button, IInspectable const& args);
|
||||
winrt::fire_and_forget _RendererWarning(IInspectable sender,
|
||||
Control::RendererWarningArgs args);
|
||||
|
||||
|
@ -115,7 +119,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged);
|
||||
TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
|
||||
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
|
||||
|
||||
|
@ -145,6 +149,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
Control::TermControlAutomationPeer _automationPeer{ nullptr };
|
||||
Control::ControlInteractivity _interactivity{ nullptr };
|
||||
Control::ControlCore _core{ nullptr };
|
||||
Control::ContentProcess _contentProc{ nullptr };
|
||||
|
||||
winrt::com_ptr<SearchBoxControl> _searchBox;
|
||||
|
||||
|
@ -183,6 +188,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
|
||||
|
||||
wil::unique_event _contentWaitInterrupt;
|
||||
std::thread _contentWaitThread;
|
||||
void _createContentWaitThread();
|
||||
bool _contentIsOutOfProc() const;
|
||||
|
||||
inline bool _IsClosing() const noexcept
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
|
@ -270,6 +280,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
|
||||
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
|
||||
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
|
||||
winrt::fire_and_forget _raiseContentDied();
|
||||
void _coreConnectionStateChanged(const IInspectable& sender, const IInspectable& args);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,17 @@ namespace Microsoft.Terminal.Control
|
|||
IMouseWheelListener,
|
||||
ICoreState
|
||||
{
|
||||
TermControl(Guid contentGuid,
|
||||
IControlSettings settings,
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
|
||||
TermControl(IControlSettings settings,
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
|
||||
static Windows.Foundation.Size GetProposedDimensions(IControlSettings settings, UInt32 dpi);
|
||||
|
||||
Guid ContentGuid{ get; };
|
||||
|
||||
void UpdateSettings();
|
||||
|
||||
Microsoft.Terminal.Control.IControlSettings Settings;
|
||||
|
|
|
@ -130,6 +130,27 @@
|
|||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Grid x:Name="ContentDiedNotice"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
x:Load="False">
|
||||
<Border Margin="8,8,8,8"
|
||||
Padding="8,8,8,8"
|
||||
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
|
||||
BorderBrush="{ThemeResource SystemAccentColor}"
|
||||
BorderThickness="2,2,2,2"
|
||||
CornerRadius="{ThemeResource OverlayCornerRadius}">
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="TermControl_ContentDiedTextBlock"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<Button x:Uid="TermControl_ContentDiedButton"
|
||||
HorizontalAlignment="Right"
|
||||
Click="_ContentDiedCloseButton_Click" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
|
|
|
@ -67,6 +67,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_contentAutomationPeer.SetControlPadding(padding);
|
||||
}
|
||||
|
||||
XamlAutomation::IRawElementProviderSimple TermControlAutomationPeer::GetParentProvider()
|
||||
{
|
||||
const auto parentProvider = this->ProviderFromPeer(*this);
|
||||
return parentProvider;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Signals the ui automation client that the terminal's selection has changed and should be updated
|
||||
// Arguments:
|
||||
|
@ -153,7 +159,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const
|
||||
{
|
||||
return RS_(L"TerminalControl_ControlType");
|
||||
// return RS_(L"TerminalControl_ControlType");
|
||||
return L"foo";
|
||||
}
|
||||
|
||||
Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
void UpdateControlBounds();
|
||||
void SetControlPadding(const Core::Padding padding);
|
||||
Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple GetParentProvider();
|
||||
|
||||
#pragma region FrameworkElementAutomationPeer
|
||||
hstring GetClassNameCore() const;
|
||||
|
|
|
@ -12,5 +12,6 @@ namespace Microsoft.Terminal.Control
|
|||
|
||||
void UpdateControlBounds();
|
||||
void SetControlPadding(Microsoft.Terminal.Core.Padding padding);
|
||||
Windows.UI.Xaml.Automation.Provider.IRawElementProviderSimple GetParentProvider();
|
||||
}
|
||||
}
|
||||
|
|
159
src/cascadia/TerminalControl/TermControlContentManagement.cpp
Normal file
159
src/cascadia/TerminalControl/TermControlContentManagement.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
//
|
||||
// The functions in this class are specific to the handling of out-of-proc
|
||||
// content processes by the TermControl. Putting them all in one file keeps
|
||||
// TermControl.cpp a little less cluttered.
|
||||
|
||||
#include "pch.h"
|
||||
#include "TermControl.h"
|
||||
|
||||
using namespace ::Microsoft::Console::Types;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
winrt::guid TermControl::ContentGuid() const
|
||||
{
|
||||
return _contentIsOutOfProc() ? _contentProc.Guid() : winrt::guid{};
|
||||
}
|
||||
|
||||
bool TermControl::_contentIsOutOfProc() const
|
||||
{
|
||||
return _contentProc != nullptr;
|
||||
}
|
||||
|
||||
bool s_waitOnContentProcess(uint64_t contentPid, HANDLE contentWaitInterrupt)
|
||||
{
|
||||
// This is the array of HANDLEs that we're going to wait on in
|
||||
// WaitForMultipleObjects below.
|
||||
// * waits[0] will be the handle to the content process. It gets
|
||||
// signalled when the process exits / dies.
|
||||
// * waits[1] is the handle to our _contentWaitInterrupt event. Another
|
||||
// thread can use that to manually break this loop. We'll do that when
|
||||
// we're getting torn down.
|
||||
HANDLE waits[2];
|
||||
waits[1] = contentWaitInterrupt;
|
||||
bool displayError = true;
|
||||
|
||||
// At any point in all this, the content process might die. If it does,
|
||||
// we want to raise an error message, to inform that this control is now
|
||||
// dead.
|
||||
try
|
||||
{
|
||||
// This might fail to even ask the content for it's PID.
|
||||
wil::unique_handle hContent{ OpenProcess(PROCESS_ALL_ACCESS,
|
||||
FALSE,
|
||||
static_cast<DWORD>(contentPid)) };
|
||||
|
||||
// If we fail to open the content, then they don't exist
|
||||
// anymore! We'll need to immediately raise the notification that the content has died.
|
||||
if (hContent.get() == nullptr)
|
||||
{
|
||||
const auto gle = GetLastError();
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_FailedToOpenContent",
|
||||
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
return displayError;
|
||||
}
|
||||
|
||||
waits[0] = hContent.get();
|
||||
|
||||
switch (WaitForMultipleObjects(2, waits, FALSE, INFINITE))
|
||||
{
|
||||
case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the content process
|
||||
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ContentDied",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
break;
|
||||
|
||||
case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt
|
||||
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ContentWaitInterrupted",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
displayError = false;
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
// This should be impossible.
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ContentWaitTimeout",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
// Returning any other value is invalid. Just die.
|
||||
const auto gle = GetLastError();
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_WaitFailed",
|
||||
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Theoretically, if window[1] dies when we're trying to get
|
||||
// it's PID we'll get here. We can probably just exit here.
|
||||
|
||||
TraceLoggingWrite(g_hTerminalControlProvider,
|
||||
"TermControl_ExceptionInWaitThread",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
return displayError;
|
||||
}
|
||||
|
||||
void TermControl::_createContentWaitThread()
|
||||
{
|
||||
_contentWaitThread = std::thread([weakThis = get_weak(), contentPid = _contentProc.GetPID(), contentWaitInterrupt = _contentWaitInterrupt.get()] {
|
||||
if (s_waitOnContentProcess(contentPid, contentWaitInterrupt))
|
||||
{
|
||||
// When s_waitOnContentProcess returns, if it returned true, we
|
||||
// should display a dialog in our bounds to indicate that we
|
||||
// were closed unexpectedly. If we closed in an expected way,
|
||||
// then s_waitOnContentProcess will return false.
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
control->_raiseContentDied();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TermControl::_raiseContentDied()
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await winrt::resume_foreground(Dispatcher());
|
||||
|
||||
if (auto control{ weakThis.get() })
|
||||
{
|
||||
if (auto loadedUiElement{ FindName(L"ContentDiedNotice") })
|
||||
{
|
||||
if (auto uiElement{ loadedUiElement.try_as<::winrt::Windows::UI::Xaml::UIElement>() })
|
||||
{
|
||||
uiElement.Visibility(Visibility::Visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Method Description:
|
||||
// - Handler for when the "Content Died" dialog's button is clicked.
|
||||
void TermControl::_ContentDiedCloseButton_Click(IInspectable const& /*sender*/, IInspectable const& /*args*/)
|
||||
{
|
||||
// Alert whoever's hosting us that the connection was closed.
|
||||
// When they come asking what the new connection state is, we'll reply with Closed
|
||||
_ConnectionStateChangedHandlers(*this, nullptr);
|
||||
}
|
||||
}
|
|
@ -28,6 +28,9 @@
|
|||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ContentProcess.h">
|
||||
<DependentUpon>ContentProcess.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControlCore.h">
|
||||
<DependentUpon>ControlCore.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
|
@ -58,13 +61,18 @@
|
|||
<ClInclude Include="TSFInputControl.h">
|
||||
<DependentUpon>TSFInputControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="XamlUiaTextRange.h" />
|
||||
<ClInclude Include="XamlUiaTextRange.h" >
|
||||
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ContentProcess.cpp">
|
||||
<DependentUpon>ContentProcess.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControlCore.cpp">
|
||||
<DependentUpon>ControlCore.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -87,6 +95,9 @@
|
|||
<ClCompile Include="TermControl.cpp">
|
||||
<DependentUpon>TermControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TermControlContentManagement.cpp">
|
||||
<DependentUpon>TermControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TSFInputControl.cpp">
|
||||
<DependentUpon>TSFInputControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
|
@ -97,10 +108,13 @@
|
|||
<ClCompile Include="InteractivityAutomationPeer.cpp">
|
||||
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="XamlUiaTextRange.cpp" />
|
||||
<ClCompile Include="XamlUiaTextRange.cpp" >
|
||||
<DependentUpon>XamlUiaTextRange.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<!-- ========================= idl Files ======================== -->
|
||||
<ItemGroup>
|
||||
<Midl Include="ContentProcess.idl" />
|
||||
<Midl Include="ControlCore.idl" />
|
||||
<Midl Include="ControlInteractivity.idl" />
|
||||
<Midl Include="ICoreState.idl" />
|
||||
|
@ -123,6 +137,7 @@
|
|||
<Midl Include="TSFInputControl.idl">
|
||||
<DependentUpon>TSFInputControl.xaml</DependentUpon>
|
||||
</Midl>
|
||||
<Midl Include="XamlUiaTextRange.idl" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= XAML Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
|
|
@ -21,13 +21,13 @@ Author(s):
|
|||
#pragma once
|
||||
|
||||
#include "TermControlAutomationPeer.h"
|
||||
#include "XamlUiaTextRange.g.h"
|
||||
#include <UIAutomationCore.h>
|
||||
#include "../types/TermControlUiaTextRange.hpp"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
class XamlUiaTextRange :
|
||||
public winrt::implements<XamlUiaTextRange, Windows::UI::Xaml::Automation::Provider::ITextRangeProvider>
|
||||
class XamlUiaTextRange : public XamlUiaTextRangeT<XamlUiaTextRange>
|
||||
{
|
||||
public:
|
||||
XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider) :
|
||||
|
|
10
src/cascadia/TerminalControl/XamlUiaTextRange.idl
Normal file
10
src/cascadia/TerminalControl/XamlUiaTextRange.idl
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
[default_interface] runtimeclass XamlUiaTextRange :
|
||||
Windows.UI.Xaml.Automation.Provider.ITextRangeProvider
|
||||
{
|
||||
}
|
||||
}
|
|
@ -108,6 +108,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
ACTION_ARG(winrt::hstring, Profile, L"");
|
||||
ACTION_ARG(Windows::Foundation::IReference<bool>, SuppressApplicationTitle, nullptr);
|
||||
ACTION_ARG(winrt::hstring, ColorScheme);
|
||||
ACTION_ARG(winrt::guid, ContentGuid);
|
||||
|
||||
static constexpr std::string_view CommandlineKey{ "commandline" };
|
||||
static constexpr std::string_view StartingDirectoryKey{ "startingDirectory" };
|
||||
|
@ -293,8 +294,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
MovePaneArgs(uint32_t& tabIndex) :
|
||||
_TabIndex{ tabIndex } {};
|
||||
ACTION_ARG(uint32_t, TabIndex, 0);
|
||||
ACTION_ARG(winrt::hstring, Window, L"");
|
||||
|
||||
static constexpr std::string_view TabIndexKey{ "index" };
|
||||
static constexpr std::string_view WindowKey{ "window" };
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
@ -304,7 +307,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
auto otherAsUs = other.try_as<MovePaneArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_TabIndex == _TabIndex;
|
||||
return otherAsUs->_TabIndex == _TabIndex && otherAsUs->_Window == _Window;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -313,6 +316,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<MovePaneArgs>();
|
||||
JsonUtils::GetValueForKey(json, TabIndexKey, args->_TabIndex);
|
||||
JsonUtils::GetValueForKey(json, WindowKey, args->_Window);
|
||||
return { *args, {} };
|
||||
}
|
||||
static Json::Value ToJson(const IActionArgs& val)
|
||||
|
@ -324,17 +328,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
Json::Value json{ Json::ValueType::objectValue };
|
||||
const auto args{ get_self<MovePaneArgs>(val) };
|
||||
JsonUtils::SetValueForKey(json, TabIndexKey, args->_TabIndex);
|
||||
JsonUtils::SetValueForKey(json, WindowKey, args->_Window);
|
||||
return json;
|
||||
}
|
||||
IActionArgs Copy() const
|
||||
{
|
||||
auto copy{ winrt::make_self<MovePaneArgs>() };
|
||||
copy->_TabIndex = _TabIndex;
|
||||
copy->_Window = _Window;
|
||||
return *copy;
|
||||
}
|
||||
size_t Hash() const
|
||||
{
|
||||
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TabIndex());
|
||||
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TabIndex(), Window());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -119,6 +119,8 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
|
||||
String ColorScheme;
|
||||
|
||||
Guid ContentGuid{ get; };
|
||||
|
||||
Boolean Equals(NewTerminalArgs other);
|
||||
String GenerateName();
|
||||
String ToCommandline();
|
||||
|
@ -147,6 +149,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
{
|
||||
MovePaneArgs(UInt32 tabIndex);
|
||||
UInt32 TabIndex;
|
||||
String Window;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SwitchToTabArgs : IActionArgs
|
||||
|
|
|
@ -22,7 +22,9 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
// and pass along the Core properties to the terminal core.
|
||||
[default_interface]
|
||||
runtimeclass TerminalSettings : Microsoft.Terminal.Core.ICoreSettings,
|
||||
Microsoft.Terminal.Control.IControlSettings
|
||||
Microsoft.Terminal.Control.IControlSettings,
|
||||
Microsoft.Terminal.Core.ICoreAppearance,
|
||||
Microsoft.Terminal.Control.IControlAppearance
|
||||
{
|
||||
TerminalSettings();
|
||||
|
||||
|
|
|
@ -214,6 +214,10 @@ void AppHost::_HandleCommandlineArgs()
|
|||
|
||||
peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId });
|
||||
|
||||
peasant.AttachRequested([this](auto&&, Remoting::AttachRequest args) {
|
||||
_logic.AttachPane(args.ContentGuid(), args.TabIndex());
|
||||
});
|
||||
|
||||
_logic.WindowName(peasant.WindowName());
|
||||
_logic.WindowId(peasant.GetID());
|
||||
}
|
||||
|
@ -271,6 +275,9 @@ void AppHost::Initialize()
|
|||
_logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged });
|
||||
_logic.IsQuakeWindowChanged({ this, &AppHost::_IsQuakeWindowChanged });
|
||||
_logic.SummonWindowRequested({ this, &AppHost::_SummonWindowRequested });
|
||||
_logic.RequestMovePane([this](auto&&, winrt::TerminalApp::RequestMovePaneArgs args) {
|
||||
_windowManager.RequestMovePane(args.Args().Window(), args.ContentGuid(), args.Args().TabIndex());
|
||||
});
|
||||
|
||||
_window->UpdateTitle(_logic.Title());
|
||||
|
||||
|
|
150
src/cascadia/WindowsTerminal/ContentProcessMain.cpp
Normal file
150
src/cascadia/WindowsTerminal/ContentProcessMain.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "AppHost.h"
|
||||
#include "resource.h"
|
||||
#include "../types/inc/User32Utils.hpp"
|
||||
#include <WilErrorReporting.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Composition;
|
||||
using namespace winrt::Windows::UI::Xaml::Hosting;
|
||||
using namespace winrt::Windows::Foundation::Numerics;
|
||||
|
||||
// We keep a weak ref to our ContentProcess singleton here.
|
||||
// Why?
|
||||
//
|
||||
// We need to always return the _same_ ContentProcess when someone comes to
|
||||
// instantiate this class. So we want to track the single instance we make. We
|
||||
// also want to track when the last outstanding reference to this object is
|
||||
// removed. If we're keeping a strong ref, then the ref count will always be > 1
|
||||
|
||||
winrt::weak_ref<winrt::Microsoft::Terminal::Control::ContentProcess> g_weak{ nullptr };
|
||||
wil::unique_event g_canExitThread;
|
||||
|
||||
struct ContentProcessFactory : implements<ContentProcessFactory, IClassFactory>
|
||||
{
|
||||
ContentProcessFactory(winrt::guid g) :
|
||||
_guid{ g } {};
|
||||
|
||||
HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final
|
||||
{
|
||||
*result = nullptr;
|
||||
if (outer)
|
||||
{
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
}
|
||||
|
||||
if (!g_weak)
|
||||
{
|
||||
// Instantiate the ContentProcess here
|
||||
winrt::Microsoft::Terminal::Control::ContentProcess strong{ _guid };
|
||||
|
||||
// Now, create a weak ref to that ContentProcess object.
|
||||
winrt::weak_ref<winrt::Microsoft::Terminal::Control::ContentProcess> weak{ strong };
|
||||
|
||||
// Stash away that weak ref for future callers.
|
||||
g_weak = weak;
|
||||
return strong.as(iid, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto strong = g_weak.get();
|
||||
// !! LOAD BEARING !! If you set this event in the _first_ branch
|
||||
// here, when we first create the object, then there will be _no_
|
||||
// referernces to the ContentProcess object for a small slice. We'll
|
||||
// stash the ContentProcess in the weak_ptr, and return it, and at
|
||||
// that moment, there will be 0 outstanding references, it'll dtor,
|
||||
// and wei'll ExitProcess.
|
||||
//
|
||||
// Instead, set the event here, once there's already a reference
|
||||
// outside of just the weak one we keep. Experimentation showed this
|
||||
// waw always hit when creating the ContentProcess at least once.
|
||||
g_canExitThread.SetEvent();
|
||||
return strong.as(iid, result);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT __stdcall LockServer(BOOL) noexcept final
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
winrt::guid _guid;
|
||||
};
|
||||
|
||||
static bool checkIfContentProcess(winrt::guid& contentProcessGuid, HANDLE& eventHandle)
|
||||
{
|
||||
std::vector<std::wstring> args;
|
||||
|
||||
if (auto commandline{ GetCommandLineW() })
|
||||
{
|
||||
int argc = 0;
|
||||
|
||||
// Get the argv, and turn them into a hstring array to pass to the app.
|
||||
wil::unique_any<LPWSTR*, decltype(&::LocalFree), ::LocalFree> argv{ CommandLineToArgvW(commandline, &argc) };
|
||||
if (argv)
|
||||
{
|
||||
for (auto& elem : wil::make_range(argv.get(), argc))
|
||||
{
|
||||
args.emplace_back(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args.size() == 5 &&
|
||||
args.at(1) == L"--content" &&
|
||||
args.at(3) == L"--signal")
|
||||
{
|
||||
auto& guidString{ args.at(2) };
|
||||
auto canConvert = guidString.length() == 38 && guidString.front() == '{' && guidString.back() == '}';
|
||||
if (canConvert)
|
||||
{
|
||||
GUID result{};
|
||||
THROW_IF_FAILED(IIDFromString(guidString.c_str(), &result));
|
||||
contentProcessGuid = result;
|
||||
|
||||
eventHandle = reinterpret_cast<HANDLE>(wcstoul(args.at(4).c_str(),
|
||||
nullptr /*endptr*/,
|
||||
16 /*base*/));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void doContentProcessThing(const winrt::guid& contentProcessGuid, const HANDLE& eventHandle)
|
||||
{
|
||||
// !! LOAD BEARING !! - important to be a MTA for these COM calls.
|
||||
winrt::init_apartment();
|
||||
DWORD registrationHostClass{};
|
||||
check_hresult(CoRegisterClassObject(contentProcessGuid,
|
||||
make<ContentProcessFactory>(contentProcessGuid).get(),
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
REGCLS_MULTIPLEUSE,
|
||||
®istrationHostClass));
|
||||
|
||||
// Signal the event handle that was passed to us that we're now set up and
|
||||
// ready to go.
|
||||
SetEvent(eventHandle);
|
||||
CloseHandle(eventHandle);
|
||||
}
|
||||
|
||||
void TryRunAsContentProcess()
|
||||
{
|
||||
winrt::guid contentProcessGuid{};
|
||||
HANDLE eventHandle{ INVALID_HANDLE_VALUE };
|
||||
if (checkIfContentProcess(contentProcessGuid, eventHandle))
|
||||
{
|
||||
g_canExitThread = wil::unique_event{ CreateEvent(nullptr, true, false, nullptr /*L"ContentProcessReady"*/) };
|
||||
|
||||
doContentProcessThing(contentProcessGuid, eventHandle);
|
||||
|
||||
WaitForSingleObject(g_canExitThread.get(), INFINITE);
|
||||
// This is the conhost thing - if we ExitThread the main thread, the
|
||||
// other threads can keep running till one calls ExitProcess.
|
||||
ExitThread(0);
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@
|
|||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="ContentProcessMain.cpp" />
|
||||
<ClCompile Include="AppHost.cpp" />
|
||||
<ClCompile Include="IslandWindow.cpp" />
|
||||
<ClCompile Include="NonClientIslandWindow.cpp" />
|
||||
|
@ -99,8 +100,8 @@
|
|||
<Reference Include="Microsoft.Terminal.Core">
|
||||
<HintPath>$(OpenConsoleCommonOutDir)TerminalCore\Microsoft.Terminal.Core.winmd</HintPath>
|
||||
<IsWinMDFile>true</IsWinMDFile>
|
||||
<Private>false</Private>
|
||||
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
|
||||
<Private>true</Private>
|
||||
<CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<!--
|
||||
|
@ -177,4 +178,4 @@
|
|||
</Target>
|
||||
<Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
|
||||
<Import Project="..\..\..\packages\Microsoft.Internal.Windows.Terminal.ThemeHelpers.0.3.210521003\build\native\Microsoft.Internal.Windows.Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Microsoft.Internal.Windows.Terminal.ThemeHelpers.0.3.210521003\build\native\Microsoft.Internal.Windows.Terminal.ThemeHelpers.targets')" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -31,6 +31,8 @@ TRACELOGGING_DEFINE_PROVIDER(
|
|||
#include <LibraryResources.h>
|
||||
UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"TerminalApp/Resources");
|
||||
|
||||
void TryRunAsContentProcess();
|
||||
|
||||
// Routine Description:
|
||||
// - Takes an image architecture and locates a string resource that maps to that architecture.
|
||||
// Arguments:
|
||||
|
@ -119,6 +121,13 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
|||
// should choose and install the correct one from the bundle.
|
||||
EnsureNativeArchitecture();
|
||||
|
||||
// If we _are_ a content process, then this function will call ExitThread(),
|
||||
// after spawning some COM threads to deal with inbound COM requests to the
|
||||
// ContentProcess object.
|
||||
TryRunAsContentProcess();
|
||||
// If we weren't a content process, then we'll just move on, and do the
|
||||
// normal WindowsTerminal thing.
|
||||
|
||||
// Make sure to call this so we get WM_POINTER messages.
|
||||
EnableMouseInPointer(true);
|
||||
|
||||
|
|
Loading…
Reference in a new issue