// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #pragma once #include "Monarch.g.h" #include "Peasant.h" #include "../cascadia/inc/cppwinrt_utils.h" #include "WindowActivatedArgs.h" #include // We sure different GUIDs here depending on whether we're running a Release, // Preview, or Dev build. This ensures that different installs don't // accidentally talk to one another. // // * Release: {06171993-7eb1-4f3e-85f5-8bdd7386cce3} // * Preview: {04221993-7eb1-4f3e-85f5-8bdd7386cce3} // * Dev: {08302020-7eb1-4f3e-85f5-8bdd7386cce3} constexpr GUID Monarch_clsid { #if defined(WT_BRANDING_RELEASE) 0x06171993, #elif defined(WT_BRANDING_PREVIEW) 0x04221993, #else 0x08302020, #endif 0x7eb1, 0x4f3e, { 0x85, 0xf5, 0x8b, 0xdd, 0x73, 0x86, 0xcc, 0xe3 } }; namespace RemotingUnitTests { class RemotingTests; }; namespace winrt::Microsoft::Terminal::Remoting::implementation { struct Monarch : public MonarchT { Monarch(); Monarch(const uint64_t testPID); ~Monarch(); uint64_t GetPID(); uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant); void SignalClose(const uint64_t peasantId); uint64_t GetNumberOfPeasants(); winrt::Microsoft::Terminal::Remoting::ProposeCommandlineResult ProposeCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& args); void HandleActivatePeasant(const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); void SummonWindow(const Remoting::SummonWindowSelectionArgs& args); void SummonAllWindows(); bool DoesQuakeWindowExist(); Windows::Foundation::Collections::IVectorView GetPeasantInfos(); TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs); TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); private: uint64_t _ourPID; std::atomic _nextPeasantID{ 1 }; uint64_t _ourPeasantId{ 0 }; // When we're quitting we do not care as much about handling some events that we know will be triggered std::atomic _quitting{ false }; winrt::com_ptr _desktopManager{ nullptr }; std::unordered_map _peasants; std::vector _mruPeasants; // These should not be locked at the same time to prevent deadlocks // unless they are both shared_locks. std::shared_mutex _peasantsMutex{}; std::shared_mutex _mruPeasantsMutex{}; winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID, bool clearMruPeasantOnFailure = true); uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop, const bool ignoreQuakeWindow); uint64_t _lookupPeasantIdForName(std::wstring_view name); void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args); void _doHandleActivatePeasant(const winrt::com_ptr& args); void _clearOldMruEntries(const std::unordered_set& peasantIds); void _forAllPeasantsIgnoringTheDead(std::function callback, std::function errorCallback); void _identifyWindows(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args); void _handleQuitAll(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& 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. // - NB: this (separately) takes unique locks on _peasantsMutex and // _mruPeasantsMutex. // 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::unordered_set peasantsToErase; { std::shared_lock lock{ _peasantsMutex }; 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(id); } else { LOG_CAUGHT_EXCEPTION(); throw; } } } } if (peasantsToErase.size() > 0) { // Don't hold a lock on _peasants and _mruPeasants at the same // time to avoid deadlocks. { std::unique_lock lock{ _peasantsMutex }; for (const auto& id : peasantsToErase) { _peasants.erase(id); } } _clearOldMruEntries(peasantsToErase); } } friend class RemotingUnitTests::RemotingTests; }; } namespace winrt::Microsoft::Terminal::Remoting::factory_implementation { BASIC_FACTORY(Monarch); }