diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 50bd7cd122..afb3e0a6e5 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -756,7 +756,7 @@ namespace winrt::TerminalApp::implementation if (!actions.empty()) { actionArgs.Handled(true); - ProcessStartupActions(std::move(actions), false); + ProcessStartupActions(std::move(actions)); } } } diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 1e4b7a3b10..1b7881dc27 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -961,18 +961,6 @@ std::vector& AppCommandlineArgs::GetStartupActions() return _startupActions; } -// Method Description: -// - Returns whether we should start listening for inbound PTY connections -// coming from the operating system default application feature. -// Arguments: -// - -// Return Value: -// - True if the listener should be started. False otherwise. -bool AppCommandlineArgs::IsHandoffListener() const noexcept -{ - return _isHandoffListener; -} - // Method Description: // - Get the string of text that should be displayed to the user on exit. This // is usually helpful for cases where the user entered some sort of invalid @@ -1015,34 +1003,28 @@ bool AppCommandlineArgs::ShouldExitEarly() const noexcept // - void AppCommandlineArgs::ValidateStartupCommands() { - // Only check over the actions list for the potential to add a new-tab - // command if we are not starting for the purposes of receiving an inbound - // handoff connection from the operating system. - if (!_isHandoffListener) + // If we only have a single x-save command, then set our target to the + // current terminal window. This will prevent us from spawning a new + // window just to save the commandline. + if (_startupActions.size() == 1 && + _startupActions.front().Action() == ShortcutAction::SaveSnippet && + _windowTarget.empty()) { - // If we only have a single x-save command, then set our target to the - // current terminal window. This will prevent us from spawning a new - // window just to save the commandline. - if (_startupActions.size() == 1 && - _startupActions.front().Action() == ShortcutAction::SaveSnippet && - _windowTarget.empty()) - { - _windowTarget = "0"; - } - // If we parsed no commands, or the first command we've parsed is not a new - // tab action, prepend a new-tab command to the front of the list. - // (also, we don't need to do this if the only action is a x-save) - else if (_startupActions.empty() || - (_startupActions.front().Action() != ShortcutAction::NewTab && - _startupActions.front().Action() != ShortcutAction::SaveSnippet)) - { - // Build the NewTab action from the values we've parsed on the commandline. - NewTerminalArgs newTerminalArgs{}; - NewTabArgs args{ newTerminalArgs }; - ActionAndArgs newTabAction{ ShortcutAction::NewTab, args }; - // push the arg onto the front - _startupActions.insert(_startupActions.begin(), 1, newTabAction); - } + _windowTarget = "0"; + } + // If we parsed no commands, or the first command we've parsed is not a new + // tab action, prepend a new-tab command to the front of the list. + // (also, we don't need to do this if the only action is a x-save) + else if (_startupActions.empty() || + (_startupActions.front().Action() != ShortcutAction::NewTab && + _startupActions.front().Action() != ShortcutAction::SaveSnippet)) + { + // Build the NewTab action from the values we've parsed on the commandline. + NewTerminalArgs newTerminalArgs{}; + NewTabArgs args{ newTerminalArgs }; + ActionAndArgs newTabAction{ ShortcutAction::NewTab, args }; + // push the arg onto the front + _startupActions.insert(_startupActions.begin(), 1, newTabAction); } } std::optional AppCommandlineArgs::GetPersistedLayoutIdx() const noexcept @@ -1082,13 +1064,9 @@ std::optional AppCommandlineArgs::GetSize() const noexcept // - 0 if the commandline was successfully parsed int AppCommandlineArgs::ParseArgs(winrt::array_view args) { - for (const auto& arg : args) + if (args.size() == 2 && args[1] == L"-Embedding") { - if (arg == L"-Embedding") - { - _isHandoffListener = true; - return 0; - } + return 0; } auto commands = ::TerminalApp::AppCommandlineArgs::BuildCommands(args); @@ -1195,7 +1173,6 @@ void AppCommandlineArgs::FullResetState() _startupActions.clear(); _exitMessage = ""; _shouldExitEarly = false; - _isHandoffListener = false; _windowTarget = {}; } diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index 9f580e957b..0a9de3999c 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -35,7 +35,6 @@ public: void ValidateStartupCommands(); std::vector& GetStartupActions(); - bool IsHandoffListener() const noexcept; const std::string& GetExitMessage() const noexcept; bool ShouldExitEarly() const noexcept; @@ -132,7 +131,6 @@ private: std::optional _launchMode{ std::nullopt }; std::optional _position{ std::nullopt }; std::optional _size{ std::nullopt }; - bool _isHandoffListener{ false }; std::vector _startupActions; std::string _exitMessage; bool _shouldExitEarly{ false }; diff --git a/src/cascadia/TerminalApp/Remoting.cpp b/src/cascadia/TerminalApp/Remoting.cpp index 0c8693ed46..078a0d7f11 100644 --- a/src/cascadia/TerminalApp/Remoting.cpp +++ b/src/cascadia/TerminalApp/Remoting.cpp @@ -15,19 +15,6 @@ using namespace winrt::Windows::Foundation; namespace winrt::TerminalApp::implementation { - CommandlineArgs::CommandlineArgs(winrt::array_view args, winrt::hstring currentDirectory, uint32_t showWindowCommand, winrt::hstring envString) : - _args{ args.begin(), args.end() }, - CurrentDirectory{ std::move(currentDirectory) }, - ShowWindowCommand{ showWindowCommand }, - CurrentEnvironment{ std::move(envString) } - { - _parseResult = _parsed.ParseArgs(_args); - if (_parseResult == 0) - { - _parsed.ValidateStartupCommands(); - } - } - ::TerminalApp::AppCommandlineArgs& CommandlineArgs::ParsedArgs() noexcept { return _parsed; @@ -56,6 +43,11 @@ namespace winrt::TerminalApp::implementation void CommandlineArgs::Commandline(const winrt::array_view& value) { _args = { value.begin(), value.end() }; + _parseResult = _parsed.ParseArgs(_args); + if (_parseResult == 0) + { + _parsed.ValidateStartupCommands(); + } } winrt::com_array CommandlineArgs::Commandline() diff --git a/src/cascadia/TerminalApp/Remoting.h b/src/cascadia/TerminalApp/Remoting.h index 2ad297f31e..9d4a48df76 100644 --- a/src/cascadia/TerminalApp/Remoting.h +++ b/src/cascadia/TerminalApp/Remoting.h @@ -13,21 +13,17 @@ namespace winrt::TerminalApp::implementation { struct CommandlineArgs : public CommandlineArgsT { - CommandlineArgs() = default; - CommandlineArgs(winrt::array_view args, winrt::hstring currentDirectory, uint32_t showWindowCommand, winrt::hstring envString); - ::TerminalApp::AppCommandlineArgs& ParsedArgs() noexcept; winrt::com_array& CommandlineRef() noexcept; // These bits are exposed via WinRT: - public: int32_t ExitCode() const noexcept; winrt::hstring ExitMessage() const; winrt::hstring TargetWindow() const; + til::property Connection; void Commandline(const winrt::array_view& value); winrt::com_array Commandline(); - til::property CurrentDirectory; til::property CurrentEnvironment; til::property ShowWindowCommand{ static_cast(SW_NORMAL) }; // SW_NORMAL is 1, 0 is SW_HIDE @@ -86,11 +82,9 @@ namespace winrt::TerminalApp::implementation WINRT_PROPERTY(uint64_t, Id); WINRT_PROPERTY(winrt::hstring, WindowName); - WINRT_PROPERTY(TerminalApp::CommandlineArgs, Command); + WINRT_PROPERTY(TerminalApp::CommandlineArgs, Command, nullptr); WINRT_PROPERTY(winrt::hstring, Content); WINRT_PROPERTY(Windows::Foundation::IReference, InitialBounds); - - private: }; } diff --git a/src/cascadia/TerminalApp/Remoting.idl b/src/cascadia/TerminalApp/Remoting.idl index 17f1318422..07fde64e50 100644 --- a/src/cascadia/TerminalApp/Remoting.idl +++ b/src/cascadia/TerminalApp/Remoting.idl @@ -6,16 +6,16 @@ namespace TerminalApp runtimeclass CommandlineArgs { CommandlineArgs(); - CommandlineArgs(String[] args, String cwd, UInt32 showWindowCommand, String env); Int32 ExitCode { get; }; String ExitMessage { get; }; String TargetWindow { get; }; + Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection; String[] Commandline; - String CurrentDirectory { get; }; - UInt32 ShowWindowCommand { get; }; - String CurrentEnvironment { get; }; + String CurrentDirectory; + UInt32 ShowWindowCommand; + String CurrentEnvironment; }; enum MonitorBehavior diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index f900e05cb7..1de587c26f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include "../../types/inc/utils.hpp" @@ -293,12 +292,11 @@ namespace winrt::TerminalApp::implementation // - true if we're not elevated but all relevant pane-spawning actions are elevated bool TerminalPage::ShouldImmediatelyHandoffToElevated(const CascadiaSettings& settings) const { - // GH#12267: Don't forget about defterm handoff here. If we're being - // created for embedding, then _yea_, we don't need to handoff to an - // elevated window. - if (_startupActions.empty() || IsRunningElevated() || _shouldStartInboundListener) + if (_startupActions.empty() || _startupConnection || IsRunningElevated()) { - // there aren't startup actions, or we're elevated. In that case, go for it. + // No point in handing off if we got no startup actions, or we're already elevated. + // Also, we shouldn't need to elevate handoff ConPTY connections. + assert(!_startupConnection); return false; } @@ -488,46 +486,16 @@ namespace winrt::TerminalApp::implementation { _startupState = StartupState::InStartup; - ProcessStartupActions(std::move(_startupActions), true); - - // If we were told that the COM server needs to be started to listen for incoming - // default application connections, start it now. - // This MUST be done after we've registered the event listener for the new connections - // or the COM server might start receiving requests on another thread and dispatch - // them to nowhere. - _StartInboundListener(); - } - } - - // Routine Description: - // - Will start the listener for inbound console handoffs if we have already determined - // that we should do so. - // NOTE: Must be after TerminalPage::_OnNewConnection has been connected up. - // Arguments: - // - - Looks at _shouldStartInboundListener - // Return Value: - // - - May fail fast if setup fails as that would leave us in a weird state. - void TerminalPage::_StartInboundListener() - { - if (_shouldStartInboundListener) - { - _shouldStartInboundListener = false; - - // Hook up inbound connection event handler - _newConnectionRevoker = ConptyConnection::NewConnection(winrt::auto_revoke, { this, &TerminalPage::_OnNewConnection }); - - try + if (_startupConnection) { - winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::StartInboundListener(); + CreateTabFromConnection(std::move(_startupConnection)); } - // If we failed to start the listener, it will throw. - // We don't want to fail fast here because if a peasant has some trouble with - // starting the listener, we don't want it to crash and take all its tabs down - // with it. - catch (...) + else if (!_startupActions.empty()) { - LOG_CAUGHT_EXCEPTION(); + ProcessStartupActions(std::move(_startupActions)); } + + _CompleteInitialization(); } } @@ -545,7 +513,7 @@ namespace winrt::TerminalApp::implementation // nt -d .` from inside another directory to work as expected. // Return Value: // - - safe_void_coroutine TerminalPage::ProcessStartupActions(std::vector actions, const bool initial, const winrt::hstring cwd, const winrt::hstring env) + safe_void_coroutine TerminalPage::ProcessStartupActions(std::vector actions, const winrt::hstring cwd, const winrt::hstring env) { const auto strong = get_strong(); @@ -597,11 +565,29 @@ namespace winrt::TerminalApp::implementation content.Focus(FocusState::Programmatic); } } + } - if (initial) + void TerminalPage::CreateTabFromConnection(ITerminalConnection connection) + { + NewTerminalArgs newTerminalArgs; + + if (const auto conpty = connection.try_as()) { - _CompleteInitialization(); + newTerminalArgs.Commandline(conpty.Commandline()); + newTerminalArgs.TabTitle(conpty.StartingTitle()); } + + // GH #12370: We absolutely cannot allow a defterm connection to + // auto-elevate. Defterm doesn't work for elevated scenarios in the + // first place. If we try accepting the connection, the spawning an + // elevated version of the Terminal with that profile... that's a + // recipe for disaster. We won't ever open up a tab in this window. + newTerminalArgs.Elevate(false); + const auto newPane = _MakePane(newTerminalArgs, nullptr, std::move(connection)); + newPane->WalkTree([](const auto& pane) { + pane->FinalizeConfigurationGivenDefault(); + }); + _CreateNewTabFromPane(newPane); } // Method Description: @@ -629,7 +615,7 @@ namespace winrt::TerminalApp::implementation // GH#12267: Make sure that we don't instantly close ourselves when // we're readying to accept a defterm connection. In that case, we don't // have a tab yet, but will once we're initialized. - if (_tabs.Size() == 0 && !_shouldStartInboundListener && !_isEmbeddingInboundListener) + if (_tabs.Size() == 0) { CloseWindowRequested.raise(*this, nullptr); co_return; @@ -3653,25 +3639,9 @@ namespace winrt::TerminalApp::implementation _startupActions = std::move(actions); } - // Routine Description: - // - Notifies this Terminal Page that it should start the incoming connection - // listener for command-line tools attempting to join this Terminal - // through the default application channel. - // Arguments: - // - isEmbedding - True if COM started us to be a server. False if we're doing it of our own accord. - // Return Value: - // - - void TerminalPage::SetInboundListener(bool isEmbedding) + void TerminalPage::SetStartupConnection(ITerminalConnection connection) { - _shouldStartInboundListener = true; - _isEmbeddingInboundListener = isEmbedding; - - // If the page has already passed the NotInitialized state, - // then it is ready-enough for us to just start this immediately. - if (_startupState != StartupState::NotInitialized) - { - _StartInboundListener(); - } + _startupConnection = std::move(connection); } winrt::TerminalApp::IDialogPresenter TerminalPage::DialogPresenter() const @@ -4073,68 +4043,6 @@ namespace winrt::TerminalApp::implementation ChangeMaximizeRequested.raise(*this, nullptr); } - HRESULT TerminalPage::_OnNewConnection(const ConptyConnection& connection) - { - _newConnectionRevoker.revoke(); - - // We need to be on the UI thread in order for _OpenNewTab to run successfully. - // HasThreadAccess will return true if we're currently on a UI thread and false otherwise. - // When we're on a COM thread, we'll need to dispatch the calls to the UI thread - // and wait on it hence the locking mechanism. - if (!Dispatcher().HasThreadAccess()) - { - til::latch latch{ 1 }; - auto finalVal = S_OK; - - Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [&]() { - finalVal = _OnNewConnection(connection); - latch.count_down(); - }); - - latch.wait(); - return finalVal; - } - - try - { - NewTerminalArgs newTerminalArgs; - newTerminalArgs.Commandline(connection.Commandline()); - newTerminalArgs.TabTitle(connection.StartingTitle()); - // GH #12370: We absolutely cannot allow a defterm connection to - // auto-elevate. Defterm doesn't work for elevated scenarios in the - // first place. If we try accepting the connection, the spawning an - // elevated version of the Terminal with that profile... that's a - // recipe for disaster. We won't ever open up a tab in this window. - newTerminalArgs.Elevate(false); - const auto newPane = _MakePane(newTerminalArgs, nullptr, connection); - newPane->WalkTree([](const auto& pane) { - pane->FinalizeConfigurationGivenDefault(); - }); - _CreateNewTabFromPane(newPane); - - // Request a summon of this window to the foreground - SummonWindowRequested.raise(*this, nullptr); - - // TEMPORARY SOLUTION - // If the connection has requested for the window to be maximized, - // manually maximize it here. Ideally, we should be _initializing_ - // the session maximized, instead of manually maximizing it after initialization. - // However, because of the current way our defterm handoff works, - // we are unable to get the connection info before the terminal session - // has already started. - - // Make sure that there were no other tabs already existing (in - // the case that we are in glomming mode), because we don't want - // to be maximizing other existing sessions that did not ask for it. - if (_tabs.Size() == 1 && connection.ShowWindow() == SW_SHOWMAXIMIZED) - { - RequestSetMaximized(true); - } - return S_OK; - } - CATCH_RETURN() - } - TerminalApp::IPaneContent TerminalPage::_makeSettingsContent() { if (auto app{ winrt::Windows::UI::Xaml::Application::Current().try_as() }) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 1fd773fb3b..4879f65fc6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -129,8 +129,8 @@ namespace winrt::TerminalApp::implementation void RequestSetMaximized(bool newMaximized); void SetStartupActions(std::vector actions); + void SetStartupConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); - void SetInboundListener(bool isEmbedding); static std::vector ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args); winrt::TerminalApp::IDialogPresenter DialogPresenter() const; @@ -147,9 +147,9 @@ namespace winrt::TerminalApp::implementation void ShowTerminalWorkingDirectory(); safe_void_coroutine ProcessStartupActions(std::vector actions, - const bool initial, const winrt::hstring cwd = winrt::hstring{}, const winrt::hstring env = winrt::hstring{}); + void CreateTabFromConnection(winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection connection); TerminalApp::WindowProperties WindowProperties() const noexcept { return _WindowProperties; }; @@ -257,8 +257,7 @@ namespace winrt::TerminalApp::implementation StartupState _startupState{ StartupState::NotInitialized }; std::vector _startupActions; - bool _shouldStartInboundListener{ false }; - bool _isEmbeddingInboundListener{ false }; + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _startupConnection{ nullptr }; std::shared_ptr _windowIdToast{ nullptr }; std::shared_ptr _actionSavedToast{ nullptr }; @@ -282,8 +281,6 @@ namespace winrt::TerminalApp::implementation winrt::Windows::Foundation::Point dragOffset{ 0, 0 }; } _stashed; - winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::NewConnection_revoker _newConnectionRevoker; - safe_void_coroutine _NewTerminalByDrop(const Windows::Foundation::IInspectable&, winrt::Windows::UI::Xaml::DragEventArgs e); __declspec(noinline) CommandPalette _loadCommandPaletteSlowPath(); @@ -469,8 +466,6 @@ namespace winrt::TerminalApp::implementation void _SetNewTabButtonColor(const Windows::UI::Color& color, const Windows::UI::Color& accentColor); void _ClearNewTabButtonColor(); - void _StartInboundListener(); - safe_void_coroutine _CompleteInitialization(); void _FocusActiveControl(IInspectable sender, IInspectable eventArgs); diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index c2afef3412..0b329a8ee4 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -152,38 +152,23 @@ namespace winrt::TerminalApp::implementation // instead. // * if we have commandline arguments, Pass commandline args into the // TerminalPage. - if (!_initialContentArgs.empty()) + if (_startupConnection) { - _root->SetStartupActions(_initialContentArgs); + _root->SetStartupConnection(std::move(_startupConnection)); } - else + else if (!_initialContentArgs.empty()) + { + _root->SetStartupActions(std::move(_initialContentArgs)); + } + else if (const auto& layout = LoadPersistedLayout()) { // layout will only ever be non-null if there were >0 tabs persisted in // .TabLayout(). We can re-evaluate that as a part of TODO: GH#12633 - if (const auto& layout = LoadPersistedLayout()) - { - std::vector actions; - for (const auto& a : layout.TabLayout()) - { - actions.emplace_back(a); - } - _root->SetStartupActions(actions); - } - else if (_appArgs) - { - _root->SetStartupActions(_appArgs->ParsedArgs().GetStartupActions()); - } + _root->SetStartupActions(wil::to_vector(layout.TabLayout())); } - - // Check if we were started as a COM server for inbound connections of console sessions - // coming out of the operating system default application feature. If so, - // tell TerminalPage to start the listener as we have to make sure it has the chance - // to register a handler to hear about the requests first and is all ready to receive - // them before the COM server registers itself. Otherwise, the request might come - // in and be routed to an event with no handlers or a non-ready Page. - if (_appArgs && _appArgs->ParsedArgs().IsHandoffListener()) + else if (_appArgs) { - _root->SetInboundListener(true); + _root->SetStartupActions(_appArgs->ParsedArgs().GetStartupActions()); } return _root->Initialize(hwnd); @@ -1054,6 +1039,7 @@ namespace winrt::TerminalApp::implementation int32_t TerminalWindow::SetStartupCommandline(TerminalApp::CommandlineArgs args) { _appArgs = winrt::get_self(args); + _startupConnection = args.Connection(); auto& parsedArgs = _appArgs->ParsedArgs(); _WindowProperties->SetInitialCwd(_appArgs->CurrentDirectory()); @@ -1114,13 +1100,16 @@ namespace winrt::TerminalApp::implementation auto& parsedArgs = _appArgs->ParsedArgs(); auto& actions = parsedArgs.GetStartupActions(); - _root->ProcessStartupActions(actions, false, _appArgs->CurrentDirectory(), _appArgs->CurrentEnvironment()); - - if (parsedArgs.IsHandoffListener()) + if (auto conn = args.Connection()) { - _root->SetInboundListener(true); + _root->CreateTabFromConnection(std::move(conn)); + } + else if (!actions.empty()) + { + _root->ProcessStartupActions(actions, _appArgs->CurrentDirectory(), _appArgs->CurrentEnvironment()); } } + // Return the result of parsing with commandline, though it may or may not be used. return _appArgs->ExitCode(); } diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index a7db7a8034..5127b3465c 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -169,6 +169,7 @@ namespace winrt::TerminalApp::implementation winrt::com_ptr _root{ nullptr }; wil::com_ptr _appArgs{ nullptr }; + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _startupConnection{ nullptr }; bool _hasCommandLineArguments{ false }; bool _gotSettingsStartupActions{ false }; std::vector _settingsStartupArgs{}; diff --git a/src/cascadia/TerminalConnection/CTerminalHandoff.cpp b/src/cascadia/TerminalConnection/CTerminalHandoff.cpp index 01b25c9ffe..c132a74615 100644 --- a/src/cascadia/TerminalConnection/CTerminalHandoff.cpp +++ b/src/cascadia/TerminalConnection/CTerminalHandoff.cpp @@ -39,7 +39,7 @@ try ComPtr unk; RETURN_IF_FAILED(classFactory.As(&unk)); - RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &g_cTerminalHandoffRegistration)); + RETURN_IF_FAILED(CoRegisterClassObject(__uuidof(CTerminalHandoff), unk.Get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_cTerminalHandoffRegistration)); return S_OK; } @@ -83,41 +83,19 @@ HRESULT CTerminalHandoff::s_StopListening() // from the registered handler event function. HRESULT CTerminalHandoff::EstablishPtyHandoff(HANDLE* in, HANDLE* out, HANDLE signal, HANDLE reference, HANDLE server, HANDLE client, const TERMINAL_STARTUP_INFO* startupInfo) { - try - { - // Because we are REGCLS_SINGLEUSE... we need to `CoRevokeClassObject` after we handle this ONE call. - // COM does not automatically clean that up for us. We must do it. - LOG_IF_FAILED(s_StopListening()); + // Report an error if no one registered a handoff function before calling this. + RETURN_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff); - // Report an error if no one registered a handoff function before calling this. - THROW_HR_IF_NULL(E_NOT_VALID_STATE, _pfnHandoff); - - // Call registered handler from when we started listening. - THROW_IF_FAILED(_pfnHandoff(in, out, signal, reference, server, client, startupInfo)); + // Call registered handler from when we started listening. + RETURN_IF_FAILED(_pfnHandoff(in, out, signal, reference, server, client, startupInfo)); #pragma warning(suppress : 26477) - TraceLoggingWrite( - g_hTerminalConnectionProvider, - "ReceiveTerminalHandoff_Success", - TraceLoggingDescription("successfully received a terminal handoff"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); + TraceLoggingWrite( + g_hTerminalConnectionProvider, + "ReceiveTerminalHandoff_Success", + TraceLoggingDescription("successfully received a terminal handoff"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - return S_OK; - } - catch (...) - { - const auto hr = wil::ResultFromCaughtException(); - -#pragma warning(suppress : 26477) - TraceLoggingWrite( - g_hTerminalConnectionProvider, - "ReceiveTerminalHandoff_Failed", - TraceLoggingDescription("failed while receiving a terminal handoff"), - TraceLoggingHResult(hr), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage)); - - return hr; - } + return S_OK; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 1e388f0f32..33d4b31cfb 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -77,6 +77,11 @@ AppHost::AppHost(WindowEmperor* manager, const winrt::TerminalApp::AppLogic& log _windowCallbacks.ShouldExitFullscreen = _window->ShouldExitFullscreen({ &_windowLogic, &winrt::TerminalApp::TerminalWindow::RequestExitFullscreen }); _window->MakeWindow(); + + // Does window creation mean the window was activated (WM_ACTIVATE)? No. + // But it simplifies `WindowEmperor::_mostRecentWindow()`, because now the creation of a + // new window marks it as the most recent one immediately, even before it becomes active. + QueryPerformanceCounter(&_lastActivatedTime); } bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down) diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 7e3ef659b6..ba128e2814 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -1242,9 +1242,12 @@ void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) // - void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args) { - auto actualDropdownDuration = args.DropdownDuration(); + const auto toggleVisibility = args ? args.ToggleVisibility() : false; + const auto toMonitor = args ? args.ToMonitor() : winrt::TerminalApp::MonitorBehavior::InPlace; + auto dropdownDuration = args ? args.DropdownDuration() : 0; + // If the user requested an animation, let's check if animations are enabled in the OS. - if (actualDropdownDuration > 0) + if (dropdownDuration > 0) { auto animationsEnabled = TRUE; SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0); @@ -1258,7 +1261,7 @@ void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args) // _globalActivateWindow/_globalDismissWindow might do if they think // there should be an animation (like making the window appear with // SetWindowPlacement rather than ShowWindow) - actualDropdownDuration = 0; + dropdownDuration = 0; } } @@ -1269,33 +1272,33 @@ void IslandWindow::SummonWindow(winrt::TerminalApp::SummonWindowBehavior args) // - activate the window // - else // - dismiss the window - if (args.ToggleVisibility() && GetForegroundWindow() == _window.get()) + if (toggleVisibility && GetForegroundWindow() == _window.get()) { auto handled = false; // They want to toggle the window when it is the FG window, and we are // the FG window. However, if we're on a different monitor than the // mouse, then we should move to that monitor instead of dismissing. - if (args.ToMonitor() == winrt::TerminalApp::MonitorBehavior::ToMouse) + if (toMonitor == winrt::TerminalApp::MonitorBehavior::ToMouse) { const til::rect cursorMonitorRect{ _getMonitorForCursor().rcMonitor }; const til::rect currentMonitorRect{ _getMonitorForWindow(GetHandle()).rcMonitor }; if (cursorMonitorRect != currentMonitorRect) { // We're not on the same monitor as the mouse. Go to that monitor. - _globalActivateWindow(actualDropdownDuration, args.ToMonitor()); + _globalActivateWindow(dropdownDuration, toMonitor); handled = true; } } if (!handled) { - _globalDismissWindow(actualDropdownDuration); + _globalDismissWindow(dropdownDuration); } } else { - _globalActivateWindow(actualDropdownDuration, args.ToMonitor()); + _globalActivateWindow(dropdownDuration, toMonitor); } } diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index 1a750af650..1bd15789ec 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -345,20 +345,31 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) for (const auto layout : layouts) { hstring args[] = { L"wt", L"-w", L"new", L"-s", winrt::to_hstring(startIdx) }; - _dispatchCommandline({ args, cwd, showCmd, env }); + _dispatchCommandlineCommon(args, cwd, env, showCmd); startIdx += 1; } } - // Create another window if needed: There aren't any yet, or we got an explicit command line. const auto args = commandlineToArgArray(GetCommandLineW()); - if (_windows.empty() || args.size() != 1) - { - _dispatchCommandline({ args, cwd, showCmd, env }); - } - // If we created no windows, e.g. because the args are "/?" we can just exit now. - _postQuitMessageIfNeeded(); + if (args.size() == 2 && args[1] == L"-Embedding") + { + // We were launched for ConPTY handoff. We have no windows and also don't want to exit. + // + // TODO: Here we could start a timer and exit after, say, 5 seconds + // if no windows are created. But that's a minor concern. + } + else + { + // Create another window if needed: There aren't any yet, OR we got an explicit command line. + if (_windows.empty() || args.size() != 1) + { + _dispatchCommandlineCommon(args, cwd, env, showCmd); + } + + // If we created no windows, e.g. because the args are "/?" we can just exit now. + _postQuitMessageIfNeeded(); + } } // ALWAYS change the _real_ CWD of the Terminal to system32, @@ -386,6 +397,19 @@ void WindowEmperor::HandleCommandlineArgs(int nCmdShow) } } + { + TerminalConnection::ConptyConnection::NewConnection([this](TerminalConnection::ConptyConnection conn) { + TerminalApp::CommandlineArgs args; + args.ShowWindowCommand(conn.ShowWindow()); + args.Connection(std::move(conn)); + _dispatchCommandline(std::move(args)); + _summonWindow(SummonWindowSelectionArgs{ + .SummonBehavior = nullptr, + }); + }); + TerminalConnection::ConptyConnection::StartInboundListener(); + } + // Main message loop. It pumps all windows. bool loggedInteraction = false; MSG msg{}; @@ -588,6 +612,16 @@ void WindowEmperor::_dispatchCommandline(winrt::TerminalApp::CommandlineArgs arg } } +void WindowEmperor::_dispatchCommandlineCommon(winrt::array_view args, std::wstring_view currentDirectory, std::wstring_view envString, uint32_t showWindowCommand) +{ + winrt::TerminalApp::CommandlineArgs c; + c.Commandline(args); + c.CurrentDirectory(currentDirectory); + c.CurrentEnvironment(envString); + c.ShowWindowCommand(showWindowCommand); + _dispatchCommandline(std::move(c)); +} + // This is an implementation-detail of _dispatchCommandline(). safe_void_coroutine WindowEmperor::_dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args) { @@ -896,10 +930,8 @@ LRESULT WindowEmperor::_messageHandler(HWND window, UINT const message, WPARAM c { const auto handoff = deserializeHandoffPayload(static_cast(cds->lpData), static_cast(cds->lpData) + cds->cbData); const winrt::hstring args{ handoff.args }; - const winrt::hstring env{ handoff.env }; - const winrt::hstring cwd{ handoff.cwd }; const auto argv = commandlineToArgArray(args.c_str()); - _dispatchCommandline({ argv, cwd, gsl::narrow_cast(handoff.show), env }); + _dispatchCommandlineCommon(argv, handoff.cwd, handoff.env, handoff.show); } return 0; case WM_HOTKEY: @@ -1198,7 +1230,7 @@ void WindowEmperor::_hotkeyPressed(const long hotkeyIndex) const wil::unique_environstrings_ptr envMem{ GetEnvironmentStringsW() }; const auto env = stringFromDoubleNullTerminated(envMem.get()); const auto cwd = wil::GetCurrentDirectoryW(); - _dispatchCommandline({ argv, cwd, SW_SHOWDEFAULT, std::move(env) }); + _dispatchCommandlineCommon(argv, cwd, env, SW_SHOWDEFAULT); } void WindowEmperor::_registerHotKey(const int index, const winrt::Microsoft::Terminal::Control::KeyChord& hotkey) noexcept diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index c84882665f..6995ab4c68 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -52,10 +52,10 @@ private: void _summonAllWindows() const; void _dispatchSpecialKey(const MSG& msg) const; void _dispatchCommandline(winrt::TerminalApp::CommandlineArgs args); + void _dispatchCommandlineCommon(winrt::array_view args, std::wstring_view currentDirectory, std::wstring_view envString, uint32_t showWindowCommand); safe_void_coroutine _dispatchCommandlineCurrentDesktop(winrt::TerminalApp::CommandlineArgs args); LRESULT _messageHandler(HWND window, UINT message, WPARAM wParam, LPARAM lParam) noexcept; void _createMessageWindow(const wchar_t* className); - bool _shouldSkipClosingWindows() const; void _postQuitMessageIfNeeded() const; safe_void_coroutine _showMessageBox(winrt::hstring message, bool error); void _notificationAreaMenuRequested(WPARAM wParam); diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 583b67a0e4..87a0ade306 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -67,8 +67,9 @@ Abstract: #include #include -#include #include +#include +#include #include #include