diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index ed7136625..886e4cb5c 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -656,6 +656,10 @@ "description": "Sets the default profile. Opens by clicking the \"+\" icon or typing the key binding assigned to \"newTab\".", "type": "string" }, + "startupActions": { + "description": "Sets the list of actions to apply if no command line is provided. Uses the same format as command line arguments", + "type": "string" + }, "disabledProfileSources": { "description": "Disables all the dynamic profile generators in this list, preventing them from adding their profiles to the list of profiles on startup.", "items": { diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 12a6223dc..234a19cc5 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -43,7 +43,8 @@ static const std::array(SettingsLoadWar USES_RESOURCE(L"FailedToParseCommandJson"), USES_RESOURCE(L"FailedToWriteToSettings"), USES_RESOURCE(L"InvalidColorSchemeInCmd"), - USES_RESOURCE(L"InvalidSplitSize") + USES_RESOURCE(L"InvalidSplitSize"), + USES_RESOURCE(L"FailedToParseStartupActions") }; static const std::array(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels { USES_RESOURCE(L"NoProfilesText"), @@ -263,6 +264,14 @@ namespace winrt::TerminalApp::implementation _settings.GlobalSettings().ShowTabsInTitlebar(false); } + // Pay attention, that even if some command line arguments were parsed (like launch mode), + // we will not use the startup actions from settings. + // While this simplifies the logic, we might want to reconsider this behavior in the future. + if (!_hasCommandLineArguments && _hasSettingsStartupActions) + { + _root->SetStartupActions(_settingsAppArgs.GetStartupActions()); + } + _root->SetSettings(_settings, false); _root->Loaded({ this, &AppLogic::_OnLoaded }); _root->Initialized([this](auto&&, auto&&) { @@ -426,8 +435,7 @@ namespace winrt::TerminalApp::implementation // Make sure the lines of text wrap warningsTextBlock.TextWrapping(TextWrapping::Wrap); - const auto warnings = _settings.Warnings(); - for (const auto& warning : warnings) + for (const auto& warning : _warnings) { // Try looking up the warning message key for each warning. const auto warningText = _GetWarningText(warning); @@ -716,7 +724,34 @@ namespace winrt::TerminalApp::implementation return E_INVALIDARG; } - hr = _settings.Warnings().Size() == 0 ? S_OK : S_FALSE; + _warnings.clear(); + for (uint32_t i = 0; i < _settings.Warnings().Size(); i++) + { + _warnings.push_back(_settings.Warnings().GetAt(i)); + } + + _hasSettingsStartupActions = false; + const auto startupActions = _settings.GlobalSettings().StartupActions(); + if (!startupActions.empty()) + { + _settingsAppArgs.FullResetState(); + + ExecuteCommandlineArgs args{ _settings.GlobalSettings().StartupActions() }; + auto result = _settingsAppArgs.ParseArgs(args); + if (result == 0) + { + _hasSettingsStartupActions = true; + + // Validation also injects new-tab command if implicit new-tab was provided. + _settingsAppArgs.ValidateStartupCommands(); + } + else + { + _warnings.push_back(SettingsLoadWarnings::FailedToParseStartupActions); + } + } + + hr = _warnings.empty() ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) { @@ -1105,6 +1140,9 @@ namespace winrt::TerminalApp::implementation const auto result = _appArgs.ParseArgs(args); if (result == 0) { + // If the size of the arguments list is 1, + // then it contains only the executable name and no other arguments. + _hasCommandLineArguments = args.size() > 1; _appArgs.ValidateStartupCommands(); _root->SetStartupActions(_appArgs.GetStartupActions()); } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 1a93abcf5..02c1029ac 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -86,6 +86,7 @@ namespace winrt::TerminalApp::implementation std::atomic _settingsReloadQueued{ false }; ::TerminalApp::AppCommandlineArgs _appArgs; + ::TerminalApp::AppCommandlineArgs _settingsAppArgs; int _ParseArgs(winrt::array_view& args); void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); @@ -107,6 +108,10 @@ namespace winrt::TerminalApp::implementation void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme); + bool _hasCommandLineArguments{ false }; + bool _hasSettingsStartupActions{ false }; + std::vector _warnings; + // These are events that are handled by the TerminalPage, but are // exposed through the AppLogic. This macro is used to forward the event // directly to them. diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 1d72c98c1..b430676ad 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -253,6 +253,10 @@ Found a "splitPane" command with an invalid "size". This command will be ignored. Make sure the size is between 0 and 1, exclusive. {Locked="\"splitPane\"","\"size\""} + + Failed to parse "startupActions". + {Locked="\"startupActions\""} + An optional command, with arguments, to be spawned in the new tab or pane diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 928f00ad4..4d2d904f8 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -40,6 +40,7 @@ static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" }; static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" }; static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" }; static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" }; +static constexpr std::string_view StartupActionsKey{ "startupActions" }; static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" }; @@ -114,6 +115,7 @@ winrt::com_ptr GlobalAppSettings::Copy() const globals->_AlwaysOnTop = _AlwaysOnTop; globals->_TabSwitcherMode = _TabSwitcherMode; globals->_DisableAnimations = _DisableAnimations; + globals->_StartupActions = _StartupActions; globals->_UnparsedDefaultProfile = _UnparsedDefaultProfile; globals->_validDefaultProfile = _validDefaultProfile; @@ -300,6 +302,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, DisableAnimationsKey, _DisableAnimations); + JsonUtils::GetValueForKey(json, StartupActionsKey, _StartupActions); + // This is a helper lambda to get the keybindings and commands out of both // and array of objects. We'll use this twice, once on the legacy // `keybindings` key, and again on the newer `bindings` key. @@ -394,6 +398,7 @@ Json::Value GlobalAppSettings::ToJson() const JsonUtils::SetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop); JsonUtils::SetValueForKey(json, TabSwitcherModeKey, _TabSwitcherMode); JsonUtils::SetValueForKey(json, DisableAnimationsKey, _DisableAnimations); + JsonUtils::SetValueForKey(json, StartupActionsKey, _StartupActions); // clang-format on // TODO GH#8100: keymap needs to be serialized here diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index d4e6c23df..8e1e65942 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -86,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GETSET_SETTING(bool, AlwaysOnTop, false); GETSET_SETTING(Model::TabSwitcherMode, TabSwitcherMode, Model::TabSwitcherMode::InOrder); GETSET_SETTING(bool, DisableAnimations, false); + GETSET_SETTING(hstring, StartupActions, L""); private: guid _defaultProfile; diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 52c22f9b5..6f0fc41da 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -142,5 +142,9 @@ namespace Microsoft.Terminal.Settings.Model KeyMapping KeyMap(); Windows.Foundation.Collections.IMapView Commands(); + + Boolean HasStartupActions(); + void ClearStartupActions(); + String StartupActions(); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl index ff8b96369..8e808d57e 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl @@ -20,6 +20,7 @@ namespace Microsoft.Terminal.Settings.Model FailedToWriteToSettings = 10, InvalidColorSchemeInCmd = 11, InvalidSplitSize = 12, + FailedToParseStartupActions = 13, WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder. }; diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 1d3226f27..372205ab0 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -26,6 +26,7 @@ "theme": "system", "snapToGridOnResize": true, "disableAnimations": false, + "startupActions": "", "profiles": [