diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 2f7a49ef0ab..6657efb67ca 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -703,6 +703,16 @@ namespace winrt::TerminalApp::implementation globals.MinimizeToNotificationArea(); } + bool AppLogic::AllowHeadless() + { + if (!_loadedInitialSettings) + { + // Load settings if we haven't already + ReloadSettings(); + } + return _settings.GlobalSettings().AllowHeadless(); + } + TerminalApp::TerminalWindow AppLogic::CreateNewWindow() { if (_settings == nullptr) diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index f5a206b1826..c3bbf12f5fe 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -67,6 +67,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::Theme Theme(); bool IsolatedMode(); + bool AllowHeadless(); bool RequestsTrayIcon(); TerminalApp::TerminalWindow CreateNewWindow(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 5eac395390f..cb5c920bbee 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -46,6 +46,7 @@ namespace TerminalApp // Selected settings to expose Microsoft.Terminal.Settings.Model.Theme Theme { get; }; Boolean IsolatedMode { get; }; + Boolean AllowHeadless { get; }; Boolean RequestsTrayIcon { get; }; FindTargetWindowResult FindTargetWindow(String[] args); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 5a32d3eebf0..a9745cf7804 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -99,6 +99,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(IVector, NewTabMenu); INHERITABLE_SETTING(Boolean, EnableColorSelection); INHERITABLE_SETTING(Boolean, IsolatedMode); + INHERITABLE_SETTING(Boolean, AllowHeadless); Windows.Foundation.Collections.IMapView ColorSchemes(); void AddColorScheme(ColorScheme scheme); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 798505de23d..84a6002d0ee 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -64,6 +64,7 @@ Author(s): X(bool, TrimPaste, "trimPaste", true) \ X(bool, EnableColorSelection, "experimental.enableColorSelection", false) \ X(winrt::Windows::Foundation::Collections::IVector, NewTabMenu, "newTabMenu", winrt::single_threaded_vector({ Model::RemainingProfilesEntry{} })) \ + X(bool, AllowHeadless, "compatibility.allowHeadless", false) \ X(bool, IsolatedMode, "compatibility.isolatedMode", false) #define MTSM_PROFILE_SETTINGS(X) \ diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.cpp b/src/cascadia/WindowsTerminal/WindowEmperor.cpp index eff02340176..4deeb38899e 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.cpp +++ b/src/cascadia/WindowsTerminal/WindowEmperor.cpp @@ -117,7 +117,8 @@ void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& _windowStartedHandlerPreXAML(); std::thread t([weakThis, window]() { - try { + try + { const auto cleanup = wil::scope_exit([&]() { if (auto self{ weakThis.lock() }) { @@ -133,7 +134,8 @@ void WindowEmperor::_createNewWindowThread(const Remoting::WindowRequestedArgs& } window->RunMessagePump(); - } CATCH_LOG() + } + CATCH_LOG() }); LOG_IF_FAILED(SetThreadDescription(t.native_handle(), L"Window Thread")); @@ -192,7 +194,12 @@ void WindowEmperor::_windowExitedHandler(uint64_t senderID) return w->Peasant().GetID() == senderID; }); - if (_windowThreadInstances.fetch_sub(1, std::memory_order_relaxed) == 1) + // When we run out of windows, exit our process if and only if: + // * We're not allowed to run headless OR + // * we've explicitly been told to "quit", which should fully exit the Terminal. + const bool quitWhenLastWindowExits{ !_app.Logic().AllowHeadless() }; + if ((_windowThreadInstances.fetch_sub(1, std::memory_order_relaxed) == 1) && // no more windows + (_quitting || quitWhenLastWindowExits)) { _close(); } @@ -287,6 +294,8 @@ void WindowEmperor::_numberOfWindowsChanged(const winrt::Windows::Foundation::II void WindowEmperor::_quitAllRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& args) { + _quitting = true; + // Make sure that the current timer is destroyed so that it doesn't attempt // to run while we are in the middle of quitting. if (_getWindowLayoutThrottler.has_value()) diff --git a/src/cascadia/WindowsTerminal/WindowEmperor.h b/src/cascadia/WindowsTerminal/WindowEmperor.h index d82b5a4a804..b2173a16ce0 100644 --- a/src/cascadia/WindowsTerminal/WindowEmperor.h +++ b/src/cascadia/WindowsTerminal/WindowEmperor.h @@ -52,8 +52,11 @@ class WindowEmperor : public std::enable_shared_from_this std::unique_ptr _notificationIcon; + bool _quitting{ false }; + void _windowStartedHandlerPreXAML(); void _windowStartedHandlerPostXAML(const std::shared_ptr& sender); + void _windowExitedHandler(uint64_t senderID); void _becomeMonarch();