diff --git a/dep/nuget/packages.config b/dep/nuget/packages.config index a641da7c51..f28aa77888 100644 --- a/dep/nuget/packages.config +++ b/dep/nuget/packages.config @@ -5,7 +5,7 @@ - + diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj index 91134e3f08..8ab41a6eca 100644 --- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj @@ -24,6 +24,7 @@ true + true diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 3b2691df93..adc3cf4150 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -1013,6 +1013,8 @@ namespace winrt::TerminalApp::implementation auto profile = tab_impl->GetFocusedProfile(); _UpdateBackground(profile); } + + _adjustProcessPriorityThrottled->Run(); } CATCH_LOG(); } diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 0896bc114a..3230bf625f 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -26,6 +26,7 @@ true true true + true diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 56d710d192..9d24ef8466 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -6,6 +6,7 @@ #include "TerminalPage.h" #include +#include #include #include @@ -102,6 +103,13 @@ namespace winrt::TerminalApp::implementation // before restoring previous tabs in that scenario. } } + + _adjustProcessPriorityThrottled = std::make_shared>( + DispatcherQueue::GetForCurrentThread(), + std::chrono::milliseconds{ 100 }, + [=]() { + _adjustProcessPriority(); + }); _hostingHwnd = hwnd; return S_OK; } @@ -1946,7 +1954,7 @@ namespace winrt::TerminalApp::implementation return false; } - TermControl TerminalPage::_GetActiveControl() + TermControl TerminalPage::_GetActiveControl() const { if (const auto terminalTab{ _GetFocusedTabImpl() }) { @@ -2410,6 +2418,8 @@ namespace winrt::TerminalApp::implementation auto profile = tab->GetFocusedProfile(); _UpdateBackground(profile); } + + _adjustProcessPriorityThrottled->Run(); } uint32_t TerminalPage::NumberOfTabs() const @@ -4611,9 +4621,12 @@ namespace winrt::TerminalApp::implementation if (const auto coreState{ sender.try_as() }) { const auto newConnectionState = coreState.ConnectionState(); + co_await wil::resume_foreground(Dispatcher()); + + _adjustProcessPriorityThrottled->Run(); + if (newConnectionState == ConnectionState::Failed && !_IsMessageDismissed(InfoBarMessage::CloseOnExitInfo)) { - co_await wil::resume_foreground(Dispatcher()); if (const auto infoBar = FindName(L"CloseOnExitInfoBar").try_as()) { infoBar.IsOpen(true); @@ -4878,6 +4891,94 @@ namespace winrt::TerminalApp::implementation } } + void TerminalPage::_adjustProcessPriority() const + { + // Windowing is single-threaded, so this will not cause a race condition. + static bool supported{ true }; + + if (!supported || !_hostingHwnd.has_value()) + { + return; + } + + std::array processes; + auto it = processes.begin(); + const auto end = processes.end(); + + auto&& appendFromControl = [&](auto&& control) { + if (it == end) + { + return; + } + if (control) + { + if (const auto conn{ control.Connection() }) + { + if (const auto pty{ conn.try_as() }) + { + if (const uint64_t process{ pty.RootProcessHandle() }; process != 0) + { + *it++ = reinterpret_cast(process); + } + } + } + } + }; + + auto&& appendFromTab = [&](auto&& tabImpl) { + if (const auto pane{ tabImpl->GetRootPane() }) + { + pane->WalkTree([&](auto&& child) { + if (const auto& control{ child->GetTerminalControl() }) + { + appendFromControl(control); + } + }); + } + }; + + if (!_activated) + { + // When a window is out of focus, we want to attach all of the processes + // under it to the window so they all go into the background at the same time. + for (auto&& tab : _tabs) + { + if (auto tabImpl{ _GetTerminalTabImpl(tab) }) + { + appendFromTab(tabImpl); + } + } + } + else + { + // When a window is in focus, propagate our foreground boost (if we have one) + // to current all panes in the current tab. + if (auto tabImpl{ _GetFocusedTabImpl() }) + { + appendFromTab(tabImpl); + } + } + + const auto count{ gsl::narrow_cast(it - processes.begin()) }; + const auto hr = TerminalTrySetWindowAssociatedProcesses(_hostingHwnd.value(), count, count ? processes.data() : nullptr); + if (S_FALSE == hr) + { + // Don't bother trying again or logging. The wrapper tells us it's unsupported. + supported = false; + return; + } + + TraceLoggingWrite( + g_hTerminalAppProvider, + "CalledNewQoSAPI", + TraceLoggingValue(reinterpret_cast(_hostingHwnd.value()), "hwnd"), + TraceLoggingValue(count), + TraceLoggingHResult(hr)); +#ifdef _DEBUG + OutputDebugStringW(fmt::format(FMT_COMPILE(L"Submitted {} processes to TerminalTrySetWindowAssociatedProcesses; return=0x{:08x}\n"), count, hr).c_str()); +#endif + } + void TerminalPage::WindowActivated(const bool activated) { // Stash if we're activated. Use that when we reload @@ -4885,6 +4986,8 @@ namespace winrt::TerminalApp::implementation _activated = activated; _updateThemeColors(); + _adjustProcessPriorityThrottled->Run(); + if (const auto& tab{ _GetFocusedTabImpl() }) { if (tab->TabStatus().IsInputBroadcastActive()) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 9c54d66248..e0b8ded229 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "TerminalPage.g.h" #include "TerminalTab.h" #include "AppKeyBindings.h" @@ -359,8 +361,11 @@ namespace winrt::TerminalApp::implementation bool _MovePane(const Microsoft::Terminal::Settings::Model::MovePaneArgs args); bool _MoveTab(winrt::com_ptr tab, const Microsoft::Terminal::Settings::Model::MoveTabArgs args); + std::shared_ptr> _adjustProcessPriorityThrottled; + void _adjustProcessPriority() const; + template - bool _ApplyToActiveControls(F f) + bool _ApplyToActiveControls(F f) const { if (const auto tab{ _GetFocusedTabImpl() }) { @@ -379,7 +384,7 @@ namespace winrt::TerminalApp::implementation return false; } - winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl(); + winrt::Microsoft::Terminal::Control::TermControl _GetActiveControl() const; std::optional _GetFocusedTabIndex() const noexcept; std::optional _GetTabIndex(const TerminalApp::TabBase& tab) const noexcept; TerminalApp::TabBase _GetFocusedTab() const noexcept; diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj index 6a20f0ac9b..4bc996863a 100644 --- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj @@ -16,6 +16,7 @@ true true + true diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index f511ff8afa..ba14ee618c 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -340,7 +340,13 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation auto ownedSignal = duplicateHandle(signal); auto ownedReference = duplicateHandle(reference); auto ownedServer = duplicateHandle(server); - auto ownedClient = duplicateHandle(client); + wil::unique_hfile ownedClient; + LOG_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), client, GetCurrentProcess(), ownedClient.addressof(), PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_SET_INFORMATION | SYNCHRONIZE, FALSE, 0)); + if (!ownedClient) + { + // If we couldn't reopen the handle with SET_INFORMATION, which may be required to do things like QoS management, fall back. + ownedClient = duplicateHandle(client); + } THROW_IF_FAILED(ConptyPackPseudoConsole(ownedServer.get(), ownedReference.get(), ownedSignal.get(), &_hPC)); ownedServer.release(); @@ -533,6 +539,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation DWORD exitCode{ 0 }; GetExitCodeProcess(_piClient.hProcess, &exitCode); + _piClient.reset(); + // Signal the closing or failure of the process. // exitCode might be STILL_ACTIVE if a client has called FreeConsole() and // thus caused the tab to close, even though the CLI app is still running. @@ -649,6 +657,12 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } } + uint64_t ConptyConnection::RootProcessHandle() noexcept + { +#pragma warning(disable : 26490) // Don't use reinterpret_cast (type.1). + return reinterpret_cast(_piClient.hProcess); + } + void ConptyConnection::Close() noexcept try { diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index cdcd99981f..f54c92e547 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -30,6 +30,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation void ShowHide(const bool show); void ReparentWindow(const uint64_t newParent); + uint64_t RootProcessHandle() noexcept; winrt::hstring Commandline() const; winrt::hstring StartingTitle() const; diff --git a/src/cascadia/TerminalConnection/ConptyConnection.idl b/src/cascadia/TerminalConnection/ConptyConnection.idl index 5264238c79..f0f074b01d 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.idl +++ b/src/cascadia/TerminalConnection/ConptyConnection.idl @@ -21,6 +21,8 @@ namespace Microsoft.Terminal.TerminalConnection void ReparentWindow(UInt64 newParent); + UInt64 RootProcessHandle(); + static event NewConnectionHandler NewConnection; static void StartInboundListener(); diff --git a/src/common.nugetversions.targets b/src/common.nugetversions.targets index 837c575fd8..fc2ee146e7 100644 --- a/src/common.nugetversions.targets +++ b/src/common.nugetversions.targets @@ -47,7 +47,7 @@ - + @@ -85,7 +85,7 @@ - + diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index cd68c921e9..49551a25bd 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -463,7 +463,7 @@ try TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); - wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE, TRUE, static_cast(connectMessage->Descriptor.Process)) }; + wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_SET_INFORMATION | SYNCHRONIZE, TRUE, static_cast(connectMessage->Descriptor.Process)) }; RETURN_LAST_ERROR_IF_NULL(clientProcess.get()); TraceLoggingWrite(g_hConhostV2EventTraceProvider,