Allow users to specify a base settings path, enabling multiple local state and multi-instance scenarios. #23034

Closed
opened 2026-01-31 08:30:26 +00:00 by claunia · 6 comments
Owner

Originally created by @dm17ryk on GitHub (Mar 17, 2025).

Description of the new feature

Multi local state and settings and multi instance with --localstate command-line argument

Summary

Introduce a new command-line option -L or --localstate to allow users to specify a custom local state directory for Windows Terminal, enabling multiple local states, settings and multi-instance scenarios.

Enhancements:

  • Modify command-line argument parsing to support the new local state option
  • Update environment variable handling to use the new local state path

Proposed technical implementation details

Reviewer Guide

Introducing the ability to run multiple instances of Windows Terminal with separate settings and state by using the --localstate command-line argument. Modify parsing to support the new local state option. The changes modify the settings path retrieval logic and window class name generation to incorporate the environment variable, allowing for isolated settings for each instance.

This pull request adds support for a new command-line argument -L or --localstate to allow users to specify a custom local state directory for Windows Terminal. The implementation involves modifying command-line argument parsing, updating environment variable handling, and updating file utilities to use the new local state path.

Sequence diagram for handling the new localstate argument

sequenceDiagram
    participant User
    participant Command Line
    participant AppCommandlineArgs
    participant WindowEmperor
    participant FileUtils

    User->>Command Line: wt --localstate <path>
    Command Line->>AppCommandlineArgs: Parse arguments
    AppCommandlineArgs->>AppCommandlineArgs: Store local state path
    AppCommandlineArgs->>WindowEmperor: Launch Terminal
    WindowEmperor->>WindowEmperor: Handle Commandline Args
    WindowEmperor->>AppCommandlineArgs: Get Local State
    WindowEmperor->>FileUtils: Set ENV_WT_BASE_SETTINGS_PATH to local state path
    FileUtils->>FileUtils: Create directories at local state path

Updated class diagram for AppCommandlineArgs

classDiagram
  class AppCommandlineArgs {
    -std::wregex _commandDelimiterRegex
    -std::vector<std::wstring> _args
    -bool _hasParseError
    -bool _shouldExitEarly
    -bool _isHandoffListener
    -int _loadPersistedLayoutIdx
    -std::string _windowTarget
    -std::string _localState
    +FullResetState()
    +GetTargetWindow() string_view
    +GetLocalState() string_view
    -_getNewTerminalArgs(NewTerminalSubcommand& subcommand) NewTerminalArgs
  }

File-Level Changes

Change Details Files
Introduced a new command-line argument -L or --localstate to allow users to specify a custom local state directory for Windows Terminal.
  • Added _localState field to AppCommandlineArgs class to store the local state path.
  • Added -L,--localstate option to the command-line argument parser.
  • Implemented GetLocalState() method in AppCommandlineArgs to retrieve the local state path.
  • Added LocalState() property to the CommandlineArgs class in Remoting.idl and implemented it in Remoting.cpp.
  • Modified WindowEmperor::HandleCommandlineArgs to retrieve the settings path from the command line arguments.
  • Updated FileUtils::GetBaseSettingsPath to retrieve the settings path from the environment variables.
src/cascadia/WindowsTerminal/WindowEmperor.cpp
src/cascadia/TerminalSettingsModel/FileUtils.cpp
src/cascadia/TerminalApp/AppCommandlineArgs.cpp
src/cascadia/TerminalApp/Remoting.cpp
src/cascadia/TerminalApp/AppCommandlineArgs.h
src/cascadia/TerminalApp/Remoting.h
src/cascadia/CascadiaPackage/Package-Dev.appxmanifest
src/cascadia/TerminalApp/Remoting.idl
src/cascadia/TerminalApp/Resources/en-US/Resources.resw

Motivation

I created Windows Terminal Layout Manager application. Which is an advanced yet user-friendly application designed to simplify and streamline the management of your Windows Terminal sessions. Effortlessly save, restore, and dynamically manage multiple terminal layouts and settings, ensuring a smooth and consistent workflow.
WTLayoutManager

Image

Image

Originally created by @dm17ryk on GitHub (Mar 17, 2025). ### Description of the new feature Multi local state and settings and multi instance with `--localstate` command-line argument ## Summary Introduce a new command-line option `-L` or `--localstate` to allow users to specify a custom local state directory for Windows Terminal, enabling multiple local states, settings and multi-instance scenarios. Enhancements: - Modify command-line argument parsing to support the new local state option - Update environment variable handling to use the new local state path ### Proposed technical implementation details ## Reviewer Guide Introducing the ability to run multiple instances of Windows Terminal with separate settings and state by using the `--localstate` command-line argument. Modify parsing to support the new local state option. The changes modify the settings path retrieval logic and window class name generation to incorporate the environment variable, allowing for isolated settings for each instance. This pull request adds support for a new command-line argument `-L` or `--localstate` to allow users to specify a custom local state directory for Windows Terminal. The implementation involves modifying command-line argument parsing, updating environment variable handling, and updating file utilities to use the new local state path. #### Sequence diagram for handling the new localstate argument ```mermaid sequenceDiagram participant User participant Command Line participant AppCommandlineArgs participant WindowEmperor participant FileUtils User->>Command Line: wt --localstate <path> Command Line->>AppCommandlineArgs: Parse arguments AppCommandlineArgs->>AppCommandlineArgs: Store local state path AppCommandlineArgs->>WindowEmperor: Launch Terminal WindowEmperor->>WindowEmperor: Handle Commandline Args WindowEmperor->>AppCommandlineArgs: Get Local State WindowEmperor->>FileUtils: Set ENV_WT_BASE_SETTINGS_PATH to local state path FileUtils->>FileUtils: Create directories at local state path ``` #### Updated class diagram for AppCommandlineArgs ```mermaid classDiagram class AppCommandlineArgs { -std::wregex _commandDelimiterRegex -std::vector<std::wstring> _args -bool _hasParseError -bool _shouldExitEarly -bool _isHandoffListener -int _loadPersistedLayoutIdx -std::string _windowTarget -std::string _localState +FullResetState() +GetTargetWindow() string_view +GetLocalState() string_view -_getNewTerminalArgs(NewTerminalSubcommand& subcommand) NewTerminalArgs } ``` ### File-Level Changes | Change | Details | Files | | ------ | ------- | ----- | | Introduced a new command-line argument `-L` or `--localstate` to allow users to specify a custom local state directory for Windows Terminal. | <ul><li>Added `_localState` field to `AppCommandlineArgs` class to store the local state path.</li><li>Added `-L,--localstate` option to the command-line argument parser.</li><li>Implemented `GetLocalState()` method in `AppCommandlineArgs` to retrieve the local state path.</li><li>Added `LocalState()` property to the `CommandlineArgs` class in `Remoting.idl` and implemented it in `Remoting.cpp`.</li><li>Modified `WindowEmperor::HandleCommandlineArgs` to retrieve the settings path from the command line arguments.</li><li>Updated `FileUtils::GetBaseSettingsPath` to retrieve the settings path from the environment variables.</li></ul> | `src/cascadia/WindowsTerminal/WindowEmperor.cpp`<br/>`src/cascadia/TerminalSettingsModel/FileUtils.cpp`<br/>`src/cascadia/TerminalApp/AppCommandlineArgs.cpp`<br/>`src/cascadia/TerminalApp/Remoting.cpp`<br/>`src/cascadia/TerminalApp/AppCommandlineArgs.h`<br/>`src/cascadia/TerminalApp/Remoting.h`<br/>`src/cascadia/CascadiaPackage/Package-Dev.appxmanifest`<br/>`src/cascadia/TerminalApp/Remoting.idl`<br/>`src/cascadia/TerminalApp/Resources/en-US/Resources.resw` | --- ## Motivation I created Windows Terminal Layout Manager application. Which is an advanced yet user-friendly application designed to simplify and streamline the management of your Windows Terminal sessions. Effortlessly save, restore, and dynamically manage multiple terminal layouts and settings, ensuring a smooth and consistent workflow. [WTLayoutManager](https://github.com/dmitrykok/WTLayoutManager) ![Image](https://github.com/user-attachments/assets/944ca59c-75be-4b5e-8df9-af04b4a23ced) ![Image](https://github.com/user-attachments/assets/94b9b7ca-49a8-4dc4-83d8-0d28ce0f775b)
claunia added the Issue-FeatureIn-PRResolution-DuplicateNeeds-Discussion labels 2026-01-31 08:30:27 +00:00
Author
Owner

@lhecker commented on GitHub (Mar 19, 2025):

Thank you! We'll need a little bit more time to look into this. This feature has been previously suggested at #6687 where the idea was to make this a CLI parameter.

@lhecker commented on GitHub (Mar 19, 2025): Thank you! We'll need a little bit more time to look into this. This feature has been previously suggested at #6687 where the idea was to make this a CLI parameter.
Author
Owner

@dm17ryk commented on GitHub (Mar 31, 2025):

Thank you! We'll need a little bit more time to look into this. This feature has been previously suggested at #6687 where the idea was to make this a CLI parameter.

I also looked if it possible to add it as CLI parameter, but as I see LocalState folder needed on very early state even before command line is parsed. So decided to use ENV variable, in order to make less changes in terminal code. And my change is, to use other LocalState folder, which will hold settings, states, buffers. So you can manage different terminal layouts and instances. I already created application for that: WTLayoutManager

@dm17ryk commented on GitHub (Mar 31, 2025): > Thank you! We'll need a little bit more time to look into this. This feature has been previously suggested at [#6687](https://github.com/microsoft/terminal/issues/6687) where the idea was to make this a CLI parameter. I also looked if it possible to add it as CLI parameter, but as I see LocalState folder needed on very early state even before command line is parsed. So decided to use ENV variable, in order to make less changes in terminal code. And my change is, to use other LocalState folder, which will hold settings, states, buffers. So you can manage different terminal layouts and instances. I already created application for that: [WTLayoutManager](https://github.com/dmitrykok/WTLayoutManager)
Author
Owner

@lhecker commented on GitHub (Mar 31, 2025):

It's definitely true that doing it with a CLI argument is a lot more difficult, but that's because it requires cleaning up the architecture. The reason your change is simple is because environment variables are essentially global variables and like any global variable it easily transmits state between all parts of the application. This results in some concerns we had, like security implications that we can't foresee yet.

The ideal solution in my mind is to move this line up: f34dbbf3ac/src/cascadia/WindowsTerminal/WindowEmperor.cpp (L352)
and to consolidate the many places that do argument parsing elsewhere into the WindowEmperor class.

However, doing so would be a huge burden for you, so the question to me personally (and perhaps the team) is how best to proceed with this PR.

@lhecker commented on GitHub (Mar 31, 2025): It's definitely true that doing it with a CLI argument is a lot more difficult, but that's because it requires cleaning up the architecture. The reason your change is simple is because environment variables are essentially global variables and like any global variable it easily transmits state between all parts of the application. This results in some concerns we had, like security implications that we can't foresee yet. The ideal solution in my mind is to move this line up: https://github.com/microsoft/terminal/blob/f34dbbf3ac1c4bd646732aa7b694553e4db97d64/src/cascadia/WindowsTerminal/WindowEmperor.cpp#L352 and to consolidate the many places that do argument parsing elsewhere into the WindowEmperor class. However, doing so would be a huge burden for you, so the question to me personally (and perhaps the team) is how best to proceed with this PR.
Author
Owner

@dm17ryk commented on GitHub (Apr 3, 2025):

It's definitely true that doing it with a CLI argument is a lot more difficult, but that's because it requires cleaning up the architecture. The reason your change is simple is because environment variables are essentially global variables and like any global variable it easily transmits state between all parts of the application. This results in some concerns we had, like security implications that we can't foresee yet.

The ideal solution in my mind is to move this line up:

terminal/src/cascadia/WindowsTerminal/WindowEmperor.cpp

Line 352 in f34dbbf

const auto args = commandlineToArgArray(GetCommandLineW());

and to consolidate the many places that do argument parsing elsewhere into the WindowEmperor class.
However, doing so would be a huge burden for you, so the question to me personally (and perhaps the team) is how best to proceed with this PR.

@lhecker, you right it was not so complicated to add --localstate command line argument and it is better this way.

@dm17ryk commented on GitHub (Apr 3, 2025): > It's definitely true that doing it with a CLI argument is a lot more difficult, but that's because it requires cleaning up the architecture. The reason your change is simple is because environment variables are essentially global variables and like any global variable it easily transmits state between all parts of the application. This results in some concerns we had, like security implications that we can't foresee yet. > > The ideal solution in my mind is to move this line up: > > [terminal/src/cascadia/WindowsTerminal/WindowEmperor.cpp](https://github.com/microsoft/terminal/blob/f34dbbf3ac1c4bd646732aa7b694553e4db97d64/src/cascadia/WindowsTerminal/WindowEmperor.cpp#L352) > > Line 352 in [f34dbbf](/microsoft/terminal/commit/f34dbbf3ac1c4bd646732aa7b694553e4db97d64) > > const auto args = commandlineToArgArray(GetCommandLineW()); > > and to consolidate the many places that do argument parsing elsewhere into the WindowEmperor class. > However, doing so would be a huge burden for you, so the question to me personally (and perhaps the team) is how best to proceed with this PR. @lhecker, you right it was not so complicated to add `--localstate` command line argument and it is better this way.
Author
Owner

@DHowett commented on GitHub (Apr 23, 2025):

Thank you so much for doing the work here, and proving that it's possible

Considering the risk of "torn settings" -- that is, launching one instance of Terminal from another and either inheriting or not-inheriting the base settings path, and having our users have to build knowledge of their settings base path into all of their terminal-based automation -- I'm going to err on the side of caution and reject this pull request.

We've written up a couple of the issues inherent in split settings paths in /dup #6687, and I'd love to track future discussion over there.

Your PR is a good start, but we're not ready to commit to supporting it right now. 🙂

@DHowett commented on GitHub (Apr 23, 2025): Thank you so much for doing the work here, and proving that it's possible Considering the risk of "torn settings" -- that is, launching one instance of Terminal from another and either inheriting or not-inheriting the base settings path, and having our users have to build knowledge of their settings base path into all of their terminal-based automation -- I'm going to err on the side of caution and reject this pull request. We've written up a couple of the issues inherent in split settings paths in /dup #6687, and I'd love to track future discussion over there. Your PR is a good start, but we're not ready to commit to supporting it right now. 🙂
Author
Owner

@dm17ryk commented on GitHub (Apr 27, 2025):

Thank you so much for doing the work here, and proving that it's possible

Considering the risk of "torn settings" -- that is, launching one instance of Terminal from another and either inheriting or not-inheriting the base settings path, and having our users have to build knowledge of their settings base path into all of their terminal-based automation -- I'm going to err on the side of caution and reject this pull request.

We've written up a couple of the issues inherent in split settings paths in /dup #6687, and I'd love to track future discussion over there.

Your PR is a good start, but we're not ready to commit to supporting it right now. 🙂

About the risk of "torn settings", currently if you run a second instance of same terminal class, it runs without any state (not loading and not saving state). And you're getting this by creating:

// Windows Terminal is a single-instance application. Either acquire ownership
// over the mutex, or hand off the command line to the existing instance.
const auto mutex = acquireMutexOrAttemptHandoff(windowClassName.c_str(), nCmdShow);

Where windowClassName build from L"Windows Terminal" (preview/canary/dev) + L" Admin" (if admin mode) and I added to it:
fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:016x}"), settingsHash);
So it runs same way, you can run only ONE INSTANCE of same class terminal with same localSettings path.

So, I think it eliminates the risk of "torn settings".

So it's very sad, that didn't look deep enough in my solution and got to wrong conclusion.

@dm17ryk commented on GitHub (Apr 27, 2025): > Thank you so much for doing the work here, and proving that it's possible > > Considering the risk of "torn settings" -- that is, launching one instance of Terminal from another and either inheriting or not-inheriting the base settings path, and having our users have to build knowledge of their settings base path into all of their terminal-based automation -- I'm going to err on the side of caution and reject this pull request. > > We've written up a couple of the issues inherent in split settings paths in /dup [#6687](https://github.com/microsoft/terminal/issues/6687), and I'd love to track future discussion over there. > > Your PR is a good start, but we're not ready to commit to supporting it right now. 🙂 About the risk of "torn settings", currently if you run a second instance of same terminal class, it runs without any state (not loading and not saving state). And you're getting this by creating: `// Windows Terminal is a single-instance application. Either acquire ownership` `// over the mutex, or hand off the command line to the existing instance.` `const auto mutex = acquireMutexOrAttemptHandoff(windowClassName.c_str(), nCmdShow);` Where `windowClassName` build from `L"Windows Terminal"` (preview/canary/dev) + `L" Admin"` (if admin mode) and I added to it: `fmt::format_to(std::back_inserter(windowClassName), FMT_COMPILE(L" {:016x}"), settingsHash);` So it runs same way, you can run only ONE INSTANCE of same class terminal with same `localSettings` path. So, I think it eliminates the risk of "torn settings". So it's very sad, that didn't look deep enough in my solution and got to wrong conclusion.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#23034