diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index 05f987661..7a7e3625c 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -613,39 +613,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation return *result; } - // Method Description: - // - Helper for doing something on each and every peasant, with no regard - // for if the peasant is living or dead. - // - We'll try calling callback on every peasant. - // - If any single peasant is dead, then we'll call errorCallback, and move on. - // - We're taking an errorCallback here, because the thing we usually want - // to do is TraceLog a message, but TraceLoggingWrite is actually a macro - // that _requires_ the second arg to be a string literal. It can't just be - // a variable. - // Arguments: - // - callback: The function to call on each peasant - // - errorCallback: The function to call if a peasant is dead. - // Return Value: - // - - void Monarch::_forAllPeasantsIgnoringTheDead(std::function callback, - std::function errorCallback) - { - for (const auto& [id, p] : _peasants) - { - try - { - callback(p, id); - } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - // If this fails, we don't _really_ care. Just move on to the - // next one. Someone else will clean up the dead peasant. - errorCallback(id); - } - } - } - // Method Description: // - This is an event handler for the IdentifyWindowsRequested event. A // Peasant may raise that event if they want _all_ windows to identify @@ -660,17 +627,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation const winrt::Windows::Foundation::IInspectable& /*args*/) { // Notify all the peasants to display their ID. - auto callback = [](auto&& p, auto&& /*id*/) { + const auto callback = [&](const auto& /*id*/, const auto& p) { p.DisplayWindowId(); }; - auto onError = [](auto&& id) { + const auto onError = [&](const auto& id) { TraceLoggingWrite(g_hRemotingProvider, "Monarch_identifyWindows_Failed", TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); }; - _forAllPeasantsIgnoringTheDead(callback, onError); + + _forEachPeasant(callback, onError); } // Method Description: @@ -812,48 +780,68 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - // Return Value: // - A map of peasant IDs to their names. - Windows::Foundation::Collections::IMapView Monarch::GetPeasantNames() + Windows::Foundation::Collections::IVectorView Monarch::GetPeasantInfos() { - auto names = winrt::single_threaded_map(); + std::vector names; + names.reserve(_peasants.size()); - std::vector peasantsToErase{}; - for (const auto& [id, p] : _peasants) - { - try + const auto func = [&](const auto& id, const auto& p) -> void { + names.push_back({ id, p.WindowName(), p.ActiveTabTitle() }); + }; + + const auto onError = [&](const auto& id) { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_identifyWindows_Failed", + TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + }; + + _forEachPeasant(func, onError); + + return winrt::single_threaded_vector(std::move(names)).GetView(); + } + + bool Monarch::DoesQuakeWindowExist() + { + bool result = false; + const auto func = [&](const auto& /*id*/, const auto& p) { + if (p.WindowName() == QuakeWindowName) { - names.Insert(id, p.WindowName()); + result = true; } - catch (...) - { - LOG_CAUGHT_EXCEPTION(); - peasantsToErase.push_back(id); - } - } + // continue if we didn't get a positive result + return !result; + }; - // Remove the dead peasants we came across while iterating. - for (const auto& id : peasantsToErase) - { - _peasants.erase(id); - _clearOldMruEntries(id); - } + const auto onError = [&](const auto& id) { + TraceLoggingWrite(g_hRemotingProvider, + "Monarch_DoesQuakeWindowExist_Failed", + TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not ask for its name"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + }; - return names.GetView(); + _forEachPeasant(func, onError); + return result; } void Monarch::SummonAllWindows() { - auto callback = [](auto&& p, auto&& /*id*/) { + const auto func = [&](const auto& /*id*/, const auto& p) { SummonWindowBehavior args{}; args.ToggleVisibility(false); p.Summon(args); }; - auto onError = [](auto&& id) { + + const auto onError = [&](const auto& id) { TraceLoggingWrite(g_hRemotingProvider, "Monarch_SummonAll_Failed", TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"), TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); }; - _forAllPeasantsIgnoringTheDead(callback, onError); + + _forEachPeasant(func, onError); } } diff --git a/src/cascadia/Remoting/Monarch.h b/src/cascadia/Remoting/Monarch.h index 2aa30dab4..80ee9cd57 100644 --- a/src/cascadia/Remoting/Monarch.h +++ b/src/cascadia/Remoting/Monarch.h @@ -53,7 +53,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); void SummonAllWindows(); - Windows::Foundation::Collections::IMapView GetPeasantNames(); + bool DoesQuakeWindowExist(); + Windows::Foundation::Collections::IVectorView GetPeasantInfos(); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); @@ -89,6 +90,67 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); + // Method Description: + // - Helper for doing something on each and every peasant. + // - We'll try calling func on every peasant. + // - If the return type of func is void, it will perform it for all peasants. + // - If the return type is a boolean, we'll break out of the loop once func + // returns false. + // - If any single peasant is dead, then we'll call onError and then add it to a + // list of peasants to clean up once the loop ends. + // Arguments: + // - func: The function to call on each peasant + // - onError: The function to call if a peasant is dead. + // Return Value: + // - + template + void _forEachPeasant(F&& func, T&& onError) + { + using Map = decltype(_peasants); + using R = std::invoke_result_t; + static constexpr auto IsVoid = std::is_void_v; + + std::vector peasantsToErase; + + for (const auto& [id, p] : _peasants) + { + try + { + if constexpr (IsVoid) + { + func(id, p); + } + else + { + if (!func(id, p)) + { + break; + } + } + } + catch (const winrt::hresult_error& exception) + { + onError(id); + + if (exception.code() == 0x800706ba) // The RPC server is unavailable. + { + peasantsToErase.emplace_back(id); + } + else + { + LOG_CAUGHT_EXCEPTION(); + throw; + } + } + } + + for (const auto& id : peasantsToErase) + { + _peasants.erase(id); + _clearOldMruEntries(id); + } + } + friend class RemotingUnitTests::RemotingTests; }; } diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index f4e8bd8b4..21a1670ac 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -31,6 +31,12 @@ namespace Microsoft.Terminal.Remoting Windows.Foundation.IReference WindowID; } + struct PeasantInfo + { + UInt64 Id; + String Name; + String TabTitle; + }; [default_interface] runtimeclass Monarch { Monarch(); @@ -42,7 +48,8 @@ namespace Microsoft.Terminal.Remoting void SummonWindow(SummonWindowSelectionArgs args); void SummonAllWindows(); - Windows.Foundation.Collections.IMapView GetPeasantNames { get; }; + Boolean DoesQuakeWindowExist(); + Windows.Foundation.Collections.IVectorView GetPeasantInfos { get; }; event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler ShowTrayIconRequested; diff --git a/src/cascadia/Remoting/Peasant.h b/src/cascadia/Remoting/Peasant.h index 6093cd835..71c02f737 100644 --- a/src/cascadia/Remoting/Peasant.h +++ b/src/cascadia/Remoting/Peasant.h @@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs(); WINRT_PROPERTY(winrt::hstring, WindowName); + WINRT_PROPERTY(winrt::hstring, ActiveTabTitle); TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs); diff --git a/src/cascadia/Remoting/Peasant.idl b/src/cascadia/Remoting/Peasant.idl index 454d748cf..d8efa7400 100644 --- a/src/cascadia/Remoting/Peasant.idl +++ b/src/cascadia/Remoting/Peasant.idl @@ -61,6 +61,7 @@ namespace Microsoft.Terminal.Remoting void DisplayWindowId(); // Tells us to display its own ID (which causes a DisplayWindowIdRequested to be raised) String WindowName { get; }; + String ActiveTabTitle { get; }; void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested void Summon(SummonWindowBehavior behavior); diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index a415bfd98..e0c4585c0 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -519,11 +519,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } - Windows::Foundation::Collections::IMapView WindowManager::GetPeasantNames() + Windows::Foundation::Collections::IVectorView WindowManager::GetPeasantInfos() { // We should only get called when we're the monarch since the monarch // is the only one that knows about all peasants. - return _monarch.GetPeasantNames(); + return _monarch.GetPeasantInfos(); } // Method Description: @@ -553,15 +553,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation bool WindowManager::DoesQuakeWindowExist() { - const auto names = GetPeasantNames(); - for (const auto [id, name] : names) - { - if (name == QuakeWindowName) - { - return true; - } - } - return false; + return _monarch.DoesQuakeWindowExist(); } + void WindowManager::UpdateActiveTabTitle(winrt::hstring title) + { + winrt::get_self(_peasant)->ActiveTabTitle(title); + } } diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 8dd52f50d..88a423b6d 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -41,11 +41,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); void SummonAllWindows(); - Windows::Foundation::Collections::IMapView GetPeasantNames(); + Windows::Foundation::Collections::IVectorView GetPeasantInfos(); winrt::fire_and_forget RequestShowTrayIcon(); winrt::fire_and_forget RequestHideTrayIcon(); bool DoesQuakeWindowExist(); + void UpdateActiveTabTitle(winrt::hstring title); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(BecameMonarch, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); diff --git a/src/cascadia/Remoting/WindowManager.idl b/src/cascadia/Remoting/WindowManager.idl index ca1f9f747..dc15f9b6e 100644 --- a/src/cascadia/Remoting/WindowManager.idl +++ b/src/cascadia/Remoting/WindowManager.idl @@ -15,8 +15,9 @@ namespace Microsoft.Terminal.Remoting void SummonAllWindows(); void RequestShowTrayIcon(); void RequestHideTrayIcon(); + void UpdateActiveTabTitle(String title); Boolean DoesQuakeWindowExist(); - Windows.Foundation.Collections.IMapView GetPeasantNames(); + Windows.Foundation.Collections.IVectorView GetPeasantInfos(); event Windows.Foundation.TypedEventHandler FindTargetWindowRequested; event Windows.Foundation.TypedEventHandler BecameMonarch; event Windows.Foundation.TypedEventHandler ShowTrayIconRequested; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 106da0fbd..229933690 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1477,4 +1477,9 @@ namespace winrt::TerminalApp::implementation return false; } } + + bool AppLogic::GetShowTitleInTitlebar() + { + return _settings.GlobalSettings().ShowTitleInTitlebar(); + } } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index dbaf301c3..86d95c8b5 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -94,6 +94,7 @@ namespace winrt::TerminalApp::implementation bool GetMinimizeToTray(); bool GetAlwaysShowTrayIcon(); + bool GetShowTitleInTitlebar(); winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 1f7675ad4..028022b45 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -72,6 +72,7 @@ namespace TerminalApp Boolean GetMinimizeToTray(); Boolean GetAlwaysShowTrayIcon(); + Boolean GetShowTitleInTitlebar(); FindTargetWindowResult FindTargetWindow(String[] args); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 3e11a9eab..32955c9a5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1019,7 +1019,7 @@ namespace winrt::TerminalApp::implementation { auto newTabTitle = tab.Title(); - if (_settings.GlobalSettings().ShowTitleInTitlebar() && tab == _GetFocusedTab()) + if (tab == _GetFocusedTab()) { _TitleChangedHandlers(*this, newTabTitle); } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index d7d21b0db..b9b358a86 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -62,6 +62,8 @@ namespace RemotingUnitTests void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; }; uint64_t GetID() { throw winrt::hresult_error{}; }; winrt::hstring WindowName() { throw winrt::hresult_error{}; }; + winrt::hstring ActiveTabTitle() { throw winrt::hresult_error{}; }; + void ActiveTabTitle(const winrt::hstring& /*value*/) { throw winrt::hresult_error{}; }; uint64_t GetPID() { throw winrt::hresult_error{}; }; bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; } void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { throw winrt::hresult_error{}; } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index b24c4fd56..e5c52d4f1 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -299,8 +299,9 @@ void AppHost::Initialize() } // Method Description: -// - Called when the app's title changes. Fires off a window message so we can -// update the window's title on the main thread. +// - Called everytime when the active tab's title changes. We'll also fire off +// a window message so we can update the window's title on the main thread, +// though we'll only do so if the settings are configured for that. // Arguments: // - sender: unused // - newTitle: the string to use as the new window title @@ -308,7 +309,11 @@ void AppHost::Initialize() // - void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle) { - _window->UpdateTitle(newTitle); + if (_logic.GetShowTitleInTitlebar()) + { + _window->UpdateTitle(newTitle); + } + _windowManager.UpdateActiveTabTitle(newTitle); } // Method Description: @@ -1031,7 +1036,7 @@ void AppHost::_CreateTrayIcon() // Hookup the handlers, save the tokens for revoking if settings change. _ReAddTrayIconToken = _window->NotifyReAddTrayIcon([this]() { _trayIcon->ReAddTrayIcon(); }); _TrayIconPressedToken = _window->NotifyTrayIconPressed([this]() { _trayIcon->TrayIconPressed(); }); - _ShowTrayContextMenuToken = _window->NotifyShowTrayContextMenu([this](til::point coord) { _trayIcon->ShowTrayContextMenu(coord, _windowManager.GetPeasantNames()); }); + _ShowTrayContextMenuToken = _window->NotifyShowTrayContextMenu([this](til::point coord) { _trayIcon->ShowTrayContextMenu(coord, _windowManager.GetPeasantInfos()); }); _TrayMenuItemSelectedToken = _window->NotifyTrayMenuItemSelected([this](HMENU hm, UINT idx) { _trayIcon->TrayMenuItemSelected(hm, idx); }); _trayIcon->SummonWindowRequested([this](auto& args) { _windowManager.SummonWindow(args); }); } diff --git a/src/cascadia/WindowsTerminal/TrayIcon.cpp b/src/cascadia/WindowsTerminal/TrayIcon.cpp index e3df4fb0b..34cc8b416 100644 --- a/src/cascadia/WindowsTerminal/TrayIcon.cpp +++ b/src/cascadia/WindowsTerminal/TrayIcon.cpp @@ -109,8 +109,8 @@ void TrayIcon::CreateTrayIcon() // - peasants: The map of all peasants that should be available in the context menu. // Return Value: // - -void TrayIcon::ShowTrayContextMenu(const til::point coord, - IMapView peasants) +void TrayIcon::ShowTrayContextMenu(const til::point& coord, + const IVectorView& peasants) { if (const auto hMenu = _CreateTrayContextMenu(peasants)) { @@ -142,7 +142,7 @@ void TrayIcon::ShowTrayContextMenu(const til::point coord, // - peasants: A map of all peasants' ID to their window name. // Return Value: // - The handle to the newly created context menu. -HMENU TrayIcon::_CreateTrayContextMenu(IMapView peasants) +HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView& peasants) { auto hMenu = CreatePopupMenu(); if (hMenu) @@ -161,17 +161,22 @@ HMENU TrayIcon::_CreateTrayContextMenu(IMapView peasan // Submenu for Windows if (auto submenu = CreatePopupMenu()) { - const auto locWindow = RS_(L"WindowIdLabel"); - const auto locUnnamed = RS_(L"UnnamedWindowName"); - for (const auto [id, name] : peasants) + for (const auto& p : peasants) { - winrt::hstring displayText = name; - if (name.empty()) + std::wstringstream displayText; + displayText << L"#" << p.Id; + + if (!p.TabTitle.empty()) { - displayText = fmt::format(L"{} {} - <{}>", locWindow, id, locUnnamed); + displayText << L": " << std::wstring_view{ p.TabTitle }; } - AppendMenu(submenu, MF_STRING, gsl::narrow(id), displayText.c_str()); + if (!p.Name.empty()) + { + displayText << L" [" << std::wstring_view{ p.Name } << L"]"; + } + + AppendMenu(submenu, MF_STRING, gsl::narrow(p.Id), displayText.str().c_str()); } MENUINFO submenuInfo{}; diff --git a/src/cascadia/WindowsTerminal/TrayIcon.h b/src/cascadia/WindowsTerminal/TrayIcon.h index 6095e56e3..2c3f9c86d 100644 --- a/src/cascadia/WindowsTerminal/TrayIcon.h +++ b/src/cascadia/WindowsTerminal/TrayIcon.h @@ -24,14 +24,14 @@ public: void ReAddTrayIcon(); void TrayIconPressed(); - void ShowTrayContextMenu(const til::point coord, winrt::Windows::Foundation::Collections::IMapView peasants); + void ShowTrayContextMenu(const til::point& coord, const winrt::Windows::Foundation::Collections::IVectorView& peasants); void TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex); WINRT_CALLBACK(SummonWindowRequested, winrt::delegate); private: void _CreateWindow(); - HMENU _CreateTrayContextMenu(winrt::Windows::Foundation::Collections::IMapView peasants); + HMENU _CreateTrayContextMenu(const winrt::Windows::Foundation::Collections::IVectorView& peasants); wil::unique_hwnd _trayIconHwnd; HWND _owningHwnd;