Skip to content

Commit

Permalink
Add support for the windowingBehavior setting (#9118)
Browse files Browse the repository at this point in the history
Adds support for the `windowingBehavior` global setting. This setting
controls how mutiple instances of `wt` behave in the absence of the `-w`
parameter. This setting has three values:
* `"useNew"`: (default) Multiple `wt` invocations (without the `-w`
  param) always create new windows. 
* `"useAnyExisting"`: When starting a new `wt`, we'll instead default to
  any existing windows. `wt -w -1` will still create new windows. 
* `"useExisting"`: Similar to `useAnyExisting`, but limits to
  windows on the current desktop. 

The IVirtualDesktopManager interface is _very_ limited. Hence why we
have to track the HWNDs manually, and ask if they're on the current
desktop. 

## Validation Steps Performed
I've been playing with it for a week now. 

References #5000
References projects/5
References #8898
Spec'd in #8135
Closes #2227
Closes https://github.com/microsoft/terminal/projects/5#card-51431448
Closes https://github.com/microsoft/terminal/projects/5#card-51431433
  • Loading branch information
zadjii-msft committed Feb 19, 2021
1 parent e6aa902 commit 69318d3
Show file tree
Hide file tree
Showing 27 changed files with 844 additions and 84 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/dictionary/apis.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ IStorage
IStringable
ITab
ITaskbar
IVirtual
LCID
llabs
llu
Expand Down
10 changes: 10 additions & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,16 @@
"type": "string"
}
]
},
"windowingBehavior": {
"default": "useNew",
"description": "Controls how new terminal instances attach to existing windows. \"useNew\" will always create a new window. \"useExisting\" will create new tabs in the most recently used window on this virtual desktop, and \"useAnyExisting\" will create tabs in the most recent window on any desktop.",
"enum": [
"useNew",
"useExisting",
"useAnyExisting"
],
"type": "string"
}
},
"required": [
Expand Down
316 changes: 271 additions & 45 deletions src/cascadia/Remoting/Monarch.cpp

Large diffs are not rendered by default.

18 changes: 8 additions & 10 deletions src/cascadia/Remoting/Monarch.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Monarch.g.h"
#include "Peasant.h"
#include "../cascadia/inc/cppwinrt_utils.h"
#include "WindowActivatedArgs.h"

// We sure different GUIDs here depending on whether we're running a Release,
// Preview, or Dev build. This ensures that different installs don't
Expand All @@ -30,12 +31,6 @@ constexpr GUID Monarch_clsid
}
};

enum class WindowingBehavior : uint64_t
{
UseNew = 0,
UseExisting = 1,
};

namespace RemotingUnitTests
{
class RemotingTests;
Expand Down Expand Up @@ -63,17 +58,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation

uint64_t _nextPeasantID{ 1 };
uint64_t _thisPeasantID{ 0 };
uint64_t _mostRecentPeasant{ 0 };
winrt::Windows::Foundation::DateTime _lastActivatedTime{};

WindowingBehavior _windowingBehavior{ WindowingBehavior::UseNew };
winrt::com_ptr<IVirtualDesktopManager> _desktopManager{ nullptr };

std::unordered_map<uint64_t, winrt::Microsoft::Terminal::Remoting::IPeasant> _peasants;

std::vector<Remoting::WindowActivatedArgs> _mruPeasants;

winrt::Microsoft::Terminal::Remoting::IPeasant _getPeasant(uint64_t peasantID);
uint64_t _getMostRecentPeasantID();
uint64_t _getMostRecentPeasantID(bool limitToCurrentDesktop);

void _peasantWindowActivated(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs& args);
void _doHandleActivatePeasant(const winrt::com_ptr<winrt::Microsoft::Terminal::Remoting::implementation::WindowActivatedArgs>& args);
void _clearOldMruEntries(const uint64_t peasantID);

friend class RemotingUnitTests::RemotingTests;
};
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/Remoting/Peasant.idl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ namespace Microsoft.Terminal.Remoting
runtimeclass WindowActivatedArgs
{
WindowActivatedArgs(UInt64 peasantID, Guid desktopID, Windows.Foundation.DateTime activatedTime);
WindowActivatedArgs(UInt64 peasantID, UInt64 hwnd, Guid desktopID, Windows.Foundation.DateTime activatedTime);
UInt64 PeasantID { get; };
UInt64 Hwnd { get; };
Guid DesktopID { get; };
Windows.Foundation.DateTime ActivatedTime { get; };
};
Expand Down
25 changes: 24 additions & 1 deletion src/cascadia/Remoting/WindowActivatedArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,40 @@ Class Name:

namespace winrt::Microsoft::Terminal::Remoting::implementation
{
struct CompareWindowActivatedArgs
{
bool operator()(const Remoting::WindowActivatedArgs& lhs, const Remoting::WindowActivatedArgs& rhs) const
{
return lhs.ActivatedTime() > rhs.ActivatedTime();
}
};
struct WindowActivatedArgs : public WindowActivatedArgsT<WindowActivatedArgs>
{
GETSET_PROPERTY(uint64_t, PeasantID, 0);
GETSET_PROPERTY(winrt::guid, DesktopID, {});
GETSET_PROPERTY(winrt::Windows::Foundation::DateTime, ActivatedTime, {});
GETSET_PROPERTY(uint64_t, Hwnd, 0);

public:
WindowActivatedArgs(uint64_t peasantID, winrt::guid desktopID, winrt::Windows::Foundation::DateTime timestamp) :
WindowActivatedArgs(uint64_t peasantID,
uint64_t hwnd,
winrt::guid desktopID,
winrt::Windows::Foundation::DateTime timestamp) :
_PeasantID{ peasantID },
_Hwnd{ hwnd },
_DesktopID{ desktopID },
_ActivatedTime{ timestamp } {};

WindowActivatedArgs(uint64_t peasantID,
winrt::guid desktopID,
winrt::Windows::Foundation::DateTime timestamp) :
WindowActivatedArgs(peasantID, 0, desktopID, timestamp){};

WindowActivatedArgs(const Remoting::WindowActivatedArgs& other) :
_PeasantID{ other.PeasantID() },
_Hwnd{ other.Hwnd() },
_DesktopID{ other.DesktopID() },
_ActivatedTime{ other.ActivatedTime() } {};
};
}

Expand Down
8 changes: 6 additions & 2 deletions src/cascadia/Remoting/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@

#pragma once

// Block minwindef.h min/max macros to prevent <algorithm> conflict
#define NOMINMAX

#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOHELP
#define NOCOMM

#include <unknwn.h>
#include <ShObjIdl.h>

// Manually include til after we include Windows.Foundation to give it winrt superpowers
#define BLOCK_TIL
#include <LibraryIncludes.h>
Expand All @@ -25,8 +31,6 @@

#include <wil/cppwinrt.h>

#include <unknwn.h>

#include <hstring.h>

#include <winrt/Windows.ApplicationModel.h>
Expand Down
16 changes: 11 additions & 5 deletions src/cascadia/TerminalApp/AppCommandlineArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ void AppCommandlineArgs::_buildParser()
maximized->excludes(fullscreen);
focus->excludes(fullscreen);

_app.add_option("-w,--window",
_windowTarget,
RS_A(L"CmdWindowTargetArgDesc"));
_app.add_option<std::optional<int>, int>("-w,--window",
_windowTarget,
RS_A(L"CmdWindowTargetArgDesc"));

// Subcommands
_buildNewTabParser();
Expand Down Expand Up @@ -854,10 +854,16 @@ void AppCommandlineArgs::FullResetState()
_exitMessage = "";
_shouldExitEarly = false;

_windowTarget = -1;
_windowTarget = std::nullopt;
}

int AppCommandlineArgs::GetTargetWindow() const noexcept
std::optional<int> AppCommandlineArgs::GetTargetWindow() const noexcept
{
// If the user provides _any_ negative number, then treat it as -1, for "use a new window".
if (_windowTarget.has_value() && *_windowTarget < 0)
{
return { -1 };
}

return _windowTarget;
}
4 changes: 2 additions & 2 deletions src/cascadia/TerminalApp/AppCommandlineArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class TerminalApp::AppCommandlineArgs final
void DisableHelpInExitMessage();
void FullResetState();

int GetTargetWindow() const noexcept;
std::optional<int> GetTargetWindow() const noexcept;

private:
static const std::wregex _commandDelimiterRegex;
Expand Down Expand Up @@ -106,7 +106,7 @@ class TerminalApp::AppCommandlineArgs final
std::string _exitMessage;
bool _shouldExitEarly{ false };

int _windowTarget{ -1 };
std::optional<int> _windowTarget{ std::nullopt };
// Are you adding more args or attributes here? If they are not reset in _resetStateToDefault, make sure to reset them in FullResetState

winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs _getNewTerminalArgs(NewTerminalSubcommand& subcommand);
Expand Down
47 changes: 32 additions & 15 deletions src/cascadia/TerminalApp/AppLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "pch.h"
#include "AppLogic.h"
#include "../inc/WindowingBehavior.h"
#include "AppLogic.g.cpp"
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>

Expand Down Expand Up @@ -1197,27 +1198,43 @@ namespace winrt::TerminalApp::implementation
// - args: an array of strings to process as a commandline. These args can contain spaces
// Return Value:
// - 0: We should handle the args "in the current window".
// - -1: We should handle the args in a new window
// - WindowingBehaviorUseNew: We should handle the args in a new window
// - WindowingBehaviorUseExisting: We should handle the args "in
// the current window ON THIS DESKTOP"
// - WindowingBehaviorUseAnyExisting: We should handle the args "in the current
// window ON ANY DESKTOP"
// - anything else: We should handle the commandline in the window with the given ID.
int32_t AppLogic::FindTargetWindow(array_view<const winrt::hstring> args)
{
::TerminalApp::AppCommandlineArgs appArgs;
const auto result = appArgs.ParseArgs(args);
if (result == 0)
{
return appArgs.GetTargetWindow();

// TODO:projects/5
//
// In the future, we'll want to use the windowingBehavior setting to
// determine what happens when a window ID wasn't manually provided.
//
// Maybe that'd be a special return value out of here, to tell the
// monarch to do something special:
//
// -1 -> create a new window
// -2 -> find the mru, this desktop
// -3 -> MRU, any desktop (is this not just 0?)
const auto parsedTarget = appArgs.GetTargetWindow();
if (parsedTarget.has_value())
{
// parsedTarget might be -1, if the user explicitly requested -1
// (or any other negative number) on the commandline. So the set
// of possible values here is {-1, 0, ℤ+}
return *parsedTarget;
}
else
{
// If the user did not provide any value on the commandline,
// then lookup our windowing behavior to determine what to do
// now.
const auto windowingBehavior = _settings.GlobalSettings().WindowingBehavior();
switch (windowingBehavior)
{
case WindowingMode::UseExisting:
return WindowingBehaviorUseExisting;
case WindowingMode::UseAnyExisting:
return WindowingBehaviorUseAnyExisting;
case WindowingMode::UseNew:
default:
return WindowingBehaviorUseNew;
}
}
}

// Any unsuccessful parse will be a new window. That new window will try
Expand All @@ -1231,7 +1248,7 @@ namespace winrt::TerminalApp::implementation
// create a new window. Then, in that new window, we'll try to set the
// StartupActions, which will again fail, returning the correct error
// message.
return -1;
return WindowingBehaviorUseNew;
}

// Method Description:
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsEditor/Launch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
InitializeComponent();

INITIALIZE_BINDABLE_ENUM_SETTING(LaunchMode, LaunchMode, LaunchMode, L"Globals_LaunchMode", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(WindowingBehavior, WindowingMode, WindowingMode, L"Globals_WindowingBehavior", L"Content");
}

void Launch::OnNavigatedTo(const NavigationEventArgs& e)
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsEditor/Launch.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
GETSET_PROPERTY(Editor::LaunchPageNavigationState, State, nullptr);

GETSET_BINDABLE_ENUM_SETTING(LaunchMode, Model::LaunchMode, State().Settings().GlobalSettings, LaunchMode);
GETSET_BINDABLE_ENUM_SETTING(WindowingBehavior, Model::WindowingMode, State().Settings().GlobalSettings, WindowingBehavior);
};
}

Expand Down
8 changes: 6 additions & 2 deletions src/cascadia/TerminalSettingsEditor/Launch.idl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import "EnumEntry.idl";
import "EnumEntry.idl";

namespace Microsoft.Terminal.Settings.Editor
{
Expand All @@ -18,6 +18,10 @@ namespace Microsoft.Terminal.Settings.Editor
IInspectable CurrentDefaultProfile;

IInspectable CurrentLaunchMode;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> LaunchModeList { get; };
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> LaunchModeList { get; };


IInspectable CurrentWindowingBehavior;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> WindowingBehaviorList { get; };
}
}
7 changes: 7 additions & 0 deletions src/cascadia/TerminalSettingsEditor/Launch.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ the MIT License. See LICENSE in the project root for license information. -->
ItemsSource="{x:Bind LaunchModeList}"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"/>
</local:SettingContainer>

<!--Launch Mode-->
<local:SettingContainer x:Uid="Globals_WindowingBehavior">
<muxc:RadioButtons SelectedItem="{x:Bind CurrentWindowingBehavior, Mode=TwoWay}"
ItemsSource="{x:Bind WindowingBehaviorList}"
ItemTemplate="{StaticResource EnumRadioButtonTemplate}"/>
</local:SettingContainer>
</StackPanel>

<!--Launch Size-->
Expand Down
15 changes: 15 additions & 0 deletions src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,21 @@
<data name="Globals_LaunchModeMaximized.Content" xml:space="preserve">
<value>Maximized</value>
</data>
<data name="Globals_WindowingBehavior.Header" xml:space="preserve">
<value>New instance behavior</value>
</data>
<data name="Globals_WindowingBehavior.HelpText" xml:space="preserve">
<value>Controls how new terminal instances attach to existing windows.</value>
</data>
<data name="Globals_WindowingBehaviorUseNew.Content" xml:space="preserve">
<value>Create a new window</value>
</data>
<data name="Globals_WindowingBehaviorUseAnyExisting.Content" xml:space="preserve">
<value>Attach to the most recently used window</value>
</data>
<data name="Globals_WindowingBehaviorUseExisting.Content" xml:space="preserve">
<value>Attach to the most recently used window on this desktop</value>
</data>
<data name="Globals_RenderingDisclaimer.Text" xml:space="preserve">
<value>These settings may be useful for troubleshooting an issue, however they will impact your performance.</value>
</data>
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/EnumMappings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
DEFINE_ENUM_MAP(Model::LaunchMode, LaunchMode);
DEFINE_ENUM_MAP(Model::TabSwitcherMode, TabSwitcherMode);
DEFINE_ENUM_MAP(Microsoft::Terminal::TerminalControl::CopyFormat, CopyFormat);
DEFINE_ENUM_MAP(Model::WindowingMode, WindowingMode);

// Profile Settings
DEFINE_ENUM_MAP(Model::CloseOnExitMode, CloseOnExitMode);
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/EnumMappings.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, LaunchMode> LaunchMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, TabSwitcherMode> TabSwitcherMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::Terminal::TerminalControl::CopyFormat> CopyFormat();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, WindowingMode> WindowingMode();

// Profile Settings
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, CloseOnExitMode> CloseOnExitMode();
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/EnumMappings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace Microsoft.Terminal.Settings.Model
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.LaunchMode> LaunchMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.TabSwitcherMode> TabSwitcherMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.TerminalControl.CopyFormat> CopyFormat { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.WindowingMode> WindowingMode { get; };

// Profile Settings
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.CloseOnExitMode> CloseOnExitMode { get; };
Expand Down
Loading

0 comments on commit 69318d3

Please sign in to comment.