Merge remote-tracking branch 'origin/main' into dev/migrie/f/non-terminal-content-elevation-warning

This commit is contained in:
Mike Griese 2021-09-13 14:20:03 -05:00
commit 4da965f901
86 changed files with 1641 additions and 271 deletions

View file

@ -536,6 +536,7 @@ DECRC
DECREQTPARM
DECRLM
DECRQM
DECRQSS
DECRST
DECSASD
DECSC

View file

@ -279,6 +279,7 @@
"paste",
"prevTab",
"renameTab",
"openSystemMenu",
"openTabRenamer",
"quakeMode",
"resetFontSize",
@ -304,6 +305,7 @@
"toggleReadOnlyMode",
"toggleShaderEffects",
"wt",
"quit",
"unbound"
],
"type": "string"
@ -1231,6 +1233,15 @@
"description": "When set to true, this enables the launch of Windows Terminal at startup. Setting this to false will disable the startup task entry. If the Windows Terminal startup task entry is disabled either by org policy or by user action this setting will have no effect.",
"type": "boolean"
},
"firstWindowPreference": {
"default": "defaultProfile",
"description": "Defines what behavior the terminal takes when it starts. \"defaultProfile\" will have the terminal launch with one tab of the default profile, and \"persistedWindowLayout\" will cause the terminal to save its layout on close and reload it on open.",
"enum": [
"defaultProfile",
"persistedWindowLayout"
],
"type": "string"
},
"launchMode": {
"default": "default",
"description": "Defines whether the terminal will launch as maximized, full screen, or in a window. Setting this to \"focus\" is equivalent to launching the terminal in the \"default\" mode, but with the focus mode enabled. Similar, setting this to \"maximizedFocus\" will result in launching the terminal in a maximized window with the focus mode enabled.",

View file

@ -2,5 +2,5 @@
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />

View file

@ -82,6 +82,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
peasant.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
peasant.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll });
_peasants[newPeasantsId] = peasant;
@ -91,6 +92,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingUInt64(newPeasantsId, "peasantID", "the ID of the new peasant"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
_WindowCreatedHandlers(nullptr, nullptr);
return newPeasantsId;
}
catch (...)
@ -107,6 +110,74 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
}
// Method Description:
// - Gives the host process an opportunity to run any pre-close logic then
// requests all peasants to close.
// Arguments:
// - <none> used
// Return Value:
// - <none>
void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
// Let the process hosting the monarch run any needed logic before
// closing all windows.
_QuitAllRequestedHandlers(*this, nullptr);
// Tell all peasants to exit.
const auto callback = [&](const auto& /*id*/, const auto& p) {
p.Quit();
};
const auto onError = [&](const auto& id) {
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_handleQuitAll_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not close"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
};
_forEachPeasant(callback, onError);
}
// Method Description:
// - Tells the monarch that a peasant is being closed.
// Arguments:
// - peasantId: the id of the peasant
// Return Value:
// - <none>
void Monarch::SignalClose(const uint64_t peasantId)
{
_peasants.erase(peasantId);
_WindowClosedHandlers(nullptr, nullptr);
}
// Method Description:
// - Counts the number of living peasants.
// Arguments:
// - <none>
// Return Value:
// - the number of active peasants.
uint64_t Monarch::GetNumberOfPeasants()
{
auto num = 0;
auto callback = [&](const auto& /*id*/, const auto& p) {
// Check that the peasant is alive, and if so increment the count
p.GetID();
num += 1;
};
auto onError = [](const auto& id) {
TraceLoggingWrite(g_hRemotingProvider,
"Monarch_GetNumberOfPeasants_Failed",
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not enumerate"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
};
_forEachPeasant(callback, onError);
return num;
}
// Method Description:
// - Event handler for the Peasant::WindowActivated event. Used as an
// opportunity for us to update our internal stack of the "most recent

View file

@ -47,6 +47,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
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);
@ -59,6 +62,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, 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;
@ -90,6 +96,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
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.

View file

@ -43,9 +43,11 @@ namespace Microsoft.Terminal.Remoting
UInt64 GetPID();
UInt64 AddPeasant(IPeasant peasant);
UInt64 GetNumberOfPeasants();
ProposeCommandlineResult ProposeCommandline(CommandlineArgs args);
void HandleActivatePeasant(WindowActivatedArgs args);
void SummonWindow(SummonWindowSelectionArgs args);
void SignalClose(UInt64 peasantId);
void SummonAllWindows();
Boolean DoesQuakeWindowExist();
@ -54,5 +56,8 @@ namespace Microsoft.Terminal.Remoting
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
};
}

View file

@ -257,4 +257,36 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::RequestQuitAll()
{
try
{
_QuitAllRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_RequestQuit",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void Peasant::Quit()
{
try
{
_QuitRequestedHandlers(*this, nullptr);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
}
TraceLoggingWrite(g_hRemotingProvider,
"Peasant_Quit",
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
}

View file

@ -30,6 +30,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
void RequestShowTrayIcon();
void RequestHideTrayIcon();
void RequestQuitAll();
void Quit();
winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs GetLastActivatedArgs();
@ -45,6 +47,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
Peasant(const uint64_t testPID);

View file

@ -67,6 +67,8 @@ namespace Microsoft.Terminal.Remoting
void Summon(SummonWindowBehavior behavior);
void RequestShowTrayIcon();
void RequestHideTrayIcon();
void RequestQuitAll();
void Quit();
event Windows.Foundation.TypedEventHandler<Object, WindowActivatedArgs> WindowActivated;
event Windows.Foundation.TypedEventHandler<Object, CommandlineArgs> ExecuteCommandlineRequested;
@ -76,6 +78,8 @@ namespace Microsoft.Terminal.Remoting
event Windows.Foundation.TypedEventHandler<Object, SummonWindowBehavior> SummonRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
};
[default_interface] runtimeclass Peasant : IPeasant

View file

@ -54,6 +54,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// monarch!
CoRevokeClassObject(_registrationHostClass);
_registrationHostClass = 0;
SignalClose();
_monarchWaitInterrupt.SetEvent();
// A thread is joinable once it's been started. Basically this just
@ -64,6 +65,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
}
}
void WindowManager::SignalClose()
{
if (_monarch)
{
try
{
_monarch.SignalClose(_peasant.GetID());
}
CATCH_LOG()
}
}
void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args)
{
// If we're the king, we _definitely_ want to process the arguments, we were
@ -250,12 +263,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// Here, we're the king!
//
// This is where you should do any additional setup that might need to be
// done when we become the king. THis will be called both for the first
// done when we become the king. This will be called both for the first
// window, and when the current monarch dies.
_monarch.WindowCreated({ get_weak(), &WindowManager::_WindowCreatedHandlers });
_monarch.WindowClosed({ get_weak(), &WindowManager::_WindowClosedHandlers });
_monarch.FindTargetWindowRequested({ this, &WindowManager::_raiseFindTargetWindowRequested });
_monarch.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequestedHandlers(*this, nullptr); });
_monarch.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequestedHandlers(*this, nullptr); });
_monarch.QuitAllRequested([this](auto&&, auto&&) { _QuitAllRequestedHandlers(*this, nullptr); });
_BecameMonarchHandlers(*this, nullptr);
}
@ -526,6 +542,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
return _monarch.GetPeasantInfos();
}
uint64_t WindowManager::GetNumberOfPeasants()
{
if (_monarch)
{
try
{
return _monarch.GetNumberOfPeasants();
}
CATCH_LOG()
}
return 0;
}
// Method Description:
// - Ask the monarch to show a tray icon.
// Arguments:
@ -551,6 +580,19 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
_peasant.RequestHideTrayIcon();
}
// Method Description:
// - Ask the monarch to quit all windows.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget WindowManager::RequestQuitAll()
{
auto strongThis{ get_strong() };
co_await winrt::resume_background();
_peasant.RequestQuitAll();
}
bool WindowManager::DoesQuakeWindowExist()
{
return _monarch.DoesQuakeWindowExist();

View file

@ -39,19 +39,25 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::Microsoft::Terminal::Remoting::Peasant CurrentWindow();
bool IsMonarch();
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
void SignalClose();
void SummonAllWindows();
uint64_t GetNumberOfPeasants();
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
winrt::fire_and_forget RequestShowTrayIcon();
winrt::fire_and_forget RequestHideTrayIcon();
winrt::fire_and_forget RequestQuitAll();
bool DoesQuakeWindowExist();
void UpdateActiveTabTitle(winrt::hstring title);
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
TYPED_EVENT(BecameMonarch, 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(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
private:
bool _shouldCreateWindow{ false };

View file

@ -8,6 +8,7 @@ namespace Microsoft.Terminal.Remoting
{
WindowManager();
void ProposeCommandline(CommandlineArgs args);
void SignalClose();
Boolean ShouldCreateWindow { get; };
IPeasant CurrentWindow();
Boolean IsMonarch { get; };
@ -15,12 +16,17 @@ namespace Microsoft.Terminal.Remoting
void SummonAllWindows();
void RequestShowTrayIcon();
void RequestHideTrayIcon();
UInt64 GetNumberOfPeasants();
void RequestQuitAll();
void UpdateActiveTabTitle(String title);
Boolean DoesQuakeWindowExist();
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos();
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> BecameMonarch;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowCreated;
event Windows.Foundation.TypedEventHandler<Object, Object> WindowClosed;
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> HideTrayIconRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
};
}

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -29,8 +29,3 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
};
}
// namespace winrt::TerminalApp::factory_implementation
// {
// BASIC_FACTORY(AdminWarningPlaceholder);
// }

View file

@ -78,7 +78,14 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
CloseWindow();
CloseWindow(false);
args.Handled(true);
}
void TerminalPage::_HandleQuit(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
RequestQuit();
args.Handled(true);
}
@ -875,6 +882,13 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleOpenSystemMenu(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
_OpenSystemMenuHandlers(*this, nullptr);
args.Handled(true);
}
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{

View file

@ -329,6 +329,14 @@ namespace winrt::TerminalApp::implementation
TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance));
}
void AppLogic::Quit()
{
if (_root)
{
_root->CloseWindow(true);
}
}
// Method Description:
// - Show a ContentDialog with buttons to take further action. Uses the
// FrameworkElements provided as the title and content of this dialog, and
@ -596,12 +604,30 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
// Use the default profile to determine how big of a window we need.
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) };
auto proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), dpi);
winrt::Windows::Foundation::Size proposedSize{};
const float scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
if (_root->ShouldUsePersistedLayout(_settings))
{
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialSize())
{
proposedSize = layouts.GetAt(0).InitialSize().Value();
// The size is saved as a non-scaled real pixel size,
// so we need to scale it appropriately.
proposedSize.Height = proposedSize.Height * scale;
proposedSize.Width = proposedSize.Width * scale;
}
}
if (proposedSize.Width == 0 && proposedSize.Height == 0)
{
// Use the default profile to determine how big of a window we need.
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, nullptr, nullptr) };
proposedSize = TermControl::GetProposedDimensions(settings.DefaultSettings(), dpi);
}
// GH#2061 - If the global setting "Always show tab bar" is
// set or if "Show tabs in title bar" is set, then we'll need to add
@ -683,7 +709,18 @@ namespace winrt::TerminalApp::implementation
LoadSettings();
}
const auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
auto initialPosition{ _settings.GlobalSettings().InitialPosition() };
if (_root->ShouldUsePersistedLayout(_settings))
{
const auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).InitialPosition())
{
initialPosition = layouts.GetAt(0).InitialPosition().Value();
}
}
return {
initialPosition.X ? initialPosition.X.Value() : defaultInitialX,
initialPosition.Y ? initialPosition.Y.Value() : defaultInitialY
@ -1126,7 +1163,7 @@ namespace winrt::TerminalApp::implementation
{
if (_root)
{
_root->CloseWindow();
_root->CloseWindow(false);
}
}
@ -1430,6 +1467,14 @@ namespace winrt::TerminalApp::implementation
}
}
void AppLogic::SetNumberOfOpenWindows(const uint64_t num)
{
if (_root)
{
_root->SetNumberOfOpenWindows(num);
}
}
void AppLogic::RenameFailed()
{
if (_root)

View file

@ -53,6 +53,8 @@ namespace winrt::TerminalApp::implementation
void LoadSettings();
[[nodiscard]] Microsoft::Terminal::Settings::Model::CascadiaSettings GetSettings() const noexcept;
void Quit();
int32_t SetStartupCommandline(array_view<const winrt::hstring> actions);
int32_t ExecuteCommandline(array_view<const winrt::hstring> actions, const winrt::hstring& cwd);
TerminalApp::FindTargetWindowResult FindTargetWindow(array_view<const winrt::hstring> actions);
@ -69,6 +71,7 @@ namespace winrt::TerminalApp::implementation
void WindowName(const winrt::hstring& name);
uint64_t WindowId();
void WindowId(const uint64_t& id);
void SetNumberOfOpenWindows(const uint64_t num);
bool IsQuakeWindow() const noexcept;
Windows::Foundation::Size GetLaunchDimensions(uint32_t dpi);
@ -172,6 +175,8 @@ namespace winrt::TerminalApp::implementation
FORWARDED_TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs, _root, RenameWindowRequested);
FORWARDED_TYPED_EVENT(IsQuakeWindowChanged, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, IsQuakeWindowChanged);
FORWARDED_TYPED_EVENT(SummonWindowRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, SummonWindowRequested);
FORWARDED_TYPED_EVENT(OpenSystemMenu, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, OpenSystemMenu);
FORWARDED_TYPED_EVENT(QuitRequested, Windows::Foundation::IInspectable, Windows::Foundation::IInspectable, _root, QuitRequested);
#ifdef UNIT_TESTING
friend class TerminalAppLocalTests::CommandlineTest;

View file

@ -39,6 +39,8 @@ namespace TerminalApp
String ParseCommandlineMessage { get; };
Boolean ShouldExitEarly { get; };
void Quit();
void LoadSettings();
Windows.UI.Xaml.UIElement GetRoot();
@ -53,6 +55,7 @@ namespace TerminalApp
void IdentifyWindow();
String WindowName;
UInt64 WindowId;
void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed();
Boolean IsQuakeWindow();
@ -96,5 +99,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> SettingsChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
}
}

View file

@ -407,7 +407,7 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Windows10version1903:Shadow="{StaticResource CommandPaletteShadow}"
CornerRadius="{ThemeResource ControlCornerRadius}"
CornerRadius="{ThemeResource OverlayCornerRadius}"
PointerPressed="_backdropPointerPressed"
Style="{ThemeResource CommandPaletteBackground}">

View file

@ -75,6 +75,167 @@ Pane::Pane(const Profile& profile, const Controls::UserControl& control, const b
});
}
// Method Description:
// - Extract the terminal settings from the current (leaf) pane's control
// to be used to create an equivalent control
// Arguments:
// - <none>
// Return Value:
// - Arguments appropriate for a SplitPane or NewTab action
NewTerminalArgs Pane::GetTerminalArgsForPane() const
{
// Leaves are the only things that have controls
assert(_IsLeaf());
NewTerminalArgs args{};
auto termControl{ _control.try_as<TermControl>() };
if (!termControl)
{
if (auto adminWarning{ _control.try_as<AdminWarningPlaceholder>() })
{
termControl = adminWarning.Content().try_as<TermControl>();
}
}
if (!termControl)
{
return nullptr;
}
auto controlSettings = termControl.Settings().as<TerminalSettings>();
args.Profile(controlSettings.ProfileName());
args.StartingDirectory(controlSettings.StartingDirectory());
args.TabTitle(controlSettings.StartingTitle());
args.Commandline(controlSettings.Commandline());
args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle());
if (controlSettings.TabColor() || controlSettings.StartingTabColor())
{
til::color c;
// StartingTabColor is prioritized over other colors
if (const auto color = controlSettings.StartingTabColor())
{
c = til::color(color.Value());
}
else
{
c = til::color(controlSettings.TabColor().Value());
}
args.TabColor(winrt::Windows::Foundation::IReference<winrt::Windows::UI::Color>(c));
}
if (controlSettings.AppliedColorScheme())
{
auto name = controlSettings.AppliedColorScheme().Name();
args.ColorScheme(name);
}
return args;
}
// Method Description:
// - Serializes the state of this tab as a series of commands that can be
// executed to recreate it.
// - This will always result in the right-most child being the focus
// after the commands finish executing.
// Arguments:
// - currentId: the id to use for the current/first pane
// - nextId: the id to use for a new pane if we split
// Return Value:
// - The state from building the startup actions, includes a vector of commands,
// the original root pane, the id of the focused pane, and the number of panes
// created.
Pane::BuildStartupState Pane::BuildStartupActions(uint32_t currentId, uint32_t nextId)
{
// if we are a leaf then all there is to do is defer to the parent.
if (_IsLeaf())
{
if (_lastActive)
{
return { {}, shared_from_this(), currentId, 0 };
}
return { {}, shared_from_this(), std::nullopt, 0 };
}
auto buildSplitPane = [&](auto newPane) {
ActionAndArgs actionAndArgs;
actionAndArgs.Action(ShortcutAction::SplitPane);
const auto terminalArgs{ newPane->GetTerminalArgsForPane() };
// When creating a pane the split size is the size of the new pane
// and not position.
SplitPaneArgs args{ SplitType::Manual, _splitState, 1. - _desiredSplitPosition, terminalArgs };
actionAndArgs.Args(args);
return actionAndArgs;
};
auto buildMoveFocus = [](auto direction) {
MoveFocusArgs args{ direction };
ActionAndArgs actionAndArgs{};
actionAndArgs.Action(ShortcutAction::MoveFocus);
actionAndArgs.Args(args);
return actionAndArgs;
};
// Handle simple case of a single split (a minor optimization for clarity)
// Here we just create the second child (by splitting) and return the first
// child for the parent to deal with.
if (_firstChild->_IsLeaf() && _secondChild->_IsLeaf())
{
auto actionAndArgs = buildSplitPane(_secondChild);
std::optional<uint32_t> focusedPaneId = std::nullopt;
if (_firstChild->_lastActive)
{
focusedPaneId = currentId;
}
else if (_secondChild->_lastActive)
{
focusedPaneId = nextId;
}
return { { actionAndArgs }, _firstChild, focusedPaneId, 1 };
}
// We now need to execute the commands for each side of the tree
// We've done one split, so the first-most child will have currentId, and the
// one after it will be incremented.
auto firstState = _firstChild->BuildStartupActions(currentId, nextId + 1);
// the next id for the second branch depends on how many splits were in the
// first child.
auto secondState = _secondChild->BuildStartupActions(nextId, nextId + firstState.panesCreated + 1);
std::vector<ActionAndArgs> actions{};
actions.reserve(firstState.args.size() + secondState.args.size() + 3);
// first we make our split
const auto newSplit = buildSplitPane(secondState.firstPane);
actions.emplace_back(std::move(newSplit));
if (firstState.args.size() > 0)
{
// Then move to the first child and execute any actions on the left branch
// then move back
actions.emplace_back(buildMoveFocus(FocusDirection::PreviousInOrder));
actions.insert(actions.end(), std::make_move_iterator(std::begin(firstState.args)), std::make_move_iterator(std::end(firstState.args)));
actions.emplace_back(buildMoveFocus(FocusDirection::NextInOrder));
}
// And if there are any commands to run on the right branch do so
if (secondState.args.size() > 0)
{
actions.insert(actions.end(), std::make_move_iterator(secondState.args.begin()), std::make_move_iterator(secondState.args.end()));
}
// if the tree is well-formed then f1.has_value and f2.has_value are
// mutually exclusive.
const auto focusedPaneId = firstState.focusedPaneId.has_value() ? firstState.focusedPaneId : secondState.focusedPaneId;
return { actions, firstState.firstPane, focusedPaneId, firstState.panesCreated + secondState.panesCreated + 1 };
}
// Method Description:
// - Update the size of this pane. Resizes each of our columns so they have the
// same relative sizes, given the newSize.

View file

@ -71,6 +71,16 @@ public:
void ClearActive();
void SetActive();
struct BuildStartupState
{
std::vector<winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs> args;
std::shared_ptr<Pane> firstPane;
std::optional<uint32_t> focusedPaneId;
uint32_t panesCreated;
};
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId);
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane() const;
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);

View file

@ -184,6 +184,9 @@
<data name="CloseAll" xml:space="preserve">
<value>Close all</value>
</data>
<data name="Quit" xml:space="preserve">
<value>Quit</value>
</data>
<data name="CloseWindowWarningTitle" xml:space="preserve">
<value>Do you want to close all tabs?</value>
</data>
@ -441,6 +444,15 @@
<value>Third-Party Notices</value>
<comment>A hyperlink name for the Terminal's third-party notices</comment>
</data>
<data name="QuitDialog.CloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="QuitDialog.PrimaryButtonText" xml:space="preserve">
<value>Close all</value>
</data>
<data name="QuitDialog.Title" xml:space="preserve">
<value>Do you want to close all windows?</value>
</data>
<data name="CloseAllDialog.CloseButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
@ -712,4 +724,10 @@
<data name="PlainText" xml:space="preserve">
<value>Plain Text</value>
</data>
<data name="CloseOnExitInfoBar.Message" xml:space="preserve">
<value>Termination behavior can be configured in advanced profile settings.</value>
</data>
<data name="InfoBarDismissButton.Content" xml:space="preserve">
<value>Don't show again</value>
</data>
</root>

View file

@ -511,6 +511,14 @@ namespace winrt::TerminalApp::implementation
// To close the window here, we need to close the hosting window.
if (_tabs.Size() == 0)
{
// If we are supposed to save state, make sure we clear it out
// if the user manually closed all tabs.
if (!_maintainStateOnTabClose && ShouldUsePersistedLayout(_settings))
{
auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(nullptr);
}
_LastTabClosedHandlers(*this, nullptr);
}
else if (focusedTabIndex.has_value() && focusedTabIndex.value() == gsl::narrow_cast<uint32_t>(tabIndex))

View file

@ -282,6 +282,21 @@ namespace winrt::TerminalApp::implementation
CATCH_LOG();
}
// Method Description;
// - Checks if the current terminal window should load or save its layout information.
// Arguments:
// - settings: The settings to use as this may be called before the page is
// fully initialized.
// Return Value:
// - true if the ApplicationState should be used.
bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const
{
// If the setting is enabled, and we are the only window.
return Feature_PersistedWindowLayout::IsEnabled() &&
settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout &&
_numOpenWindows == 1;
}
winrt::fire_and_forget TerminalPage::NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e)
{
Windows::Foundation::Collections::IVectorView<Windows::Storage::IStorageItem> items;
@ -364,6 +379,34 @@ namespace winrt::TerminalApp::implementation
if (_startupState == StartupState::NotInitialized)
{
_startupState = StartupState::InStartup;
// If the user selected to save their tab layout, we are the first
// window opened, and wt was not run with any other arguments, then
// we should use the saved settings.
auto firstActionIsDefault = [](ActionAndArgs action) {
if (action.Action() != ShortcutAction::NewTab)
{
return false;
}
// If no commands were given, we will have default args
if (const auto args = action.Args().try_as<NewTabArgs>())
{
NewTerminalArgs defaultArgs{};
return args.TerminalArgs() == nullptr || args.TerminalArgs().Equals(defaultArgs);
}
return false;
};
if (ShouldUsePersistedLayout(_settings) && _startupActions.Size() == 1 && firstActionIsDefault(_startupActions.GetAt(0)))
{
auto layouts = ApplicationState::SharedInstance().PersistedWindowLayouts();
if (layouts && layouts.Size() > 0 && layouts.GetAt(0).TabLayout() && layouts.GetAt(0).TabLayout().Size() > 0)
{
_startupActions = layouts.GetAt(0).TabLayout();
}
}
ProcessStartupActions(_startupActions, true);
// If we were told that the COM server needs to be started to listen for incoming
@ -521,6 +564,21 @@ namespace winrt::TerminalApp::implementation
ShellExecute(nullptr, nullptr, currentPath.c_str(), nullptr, nullptr, SW_SHOW);
}
// Method Description:
// - Displays a dialog to warn the user that they are about to close all open windows.
// Once the user clicks the OK button, shut down the application.
// If cancel is clicked, the dialog will close.
// - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens. See _ShowDialog for details
winrt::Windows::Foundation::IAsyncOperation<ContentDialogResult> TerminalPage::_ShowQuitDialog()
{
if (auto presenter{ _dialogPresenter.get() })
{
co_return co_await presenter.ShowDialog(FindName(L"QuitDialog").try_as<WUX::Controls::ContentDialog>());
}
co_return ContentDialogResult::None;
}
// Method Description:
// - Displays a dialog for warnings found while closing the terminal app using
// key binding with multiple tabs opened. Display messages to warn user
@ -1065,6 +1123,8 @@ namespace winrt::TerminalApp::implementation
// Add an event handler for when the terminal or tab wants to set a
// progress indicator on the taskbar
term.SetTaskbarProgress({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
term.ConnectionStateChanged({ get_weak(), &TerminalPage::_ConnectionStateChangedHandler });
}
// Method Description:
@ -1209,13 +1269,116 @@ namespace winrt::TerminalApp::implementation
}
return nullptr;
}
// Method Description:
// - Warn the user that they are about to close all open windows, then
// signal that we want to close everything.
fire_and_forget TerminalPage::RequestQuit()
{
if (!_displayingCloseDialog)
{
_displayingCloseDialog = true;
ContentDialogResult warningResult = co_await _ShowQuitDialog();
_displayingCloseDialog = false;
if (warningResult != ContentDialogResult::Primary)
{
co_return;
}
_QuitRequestedHandlers(nullptr, nullptr);
}
}
// Method Description:
// - Saves the window position and tab layout to the application state
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::PersistWindowLayout()
{
std::vector<ActionAndArgs> actions;
for (auto tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
{
auto tabActions = terminalTab->BuildStartupActions();
actions.insert(actions.end(), tabActions.begin(), tabActions.end());
}
else if (tab.try_as<SettingsTab>())
{
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args);
actions.push_back(action);
}
}
// if the focused tab was not the last tab, restore that
auto idx = _GetFocusedTabIndex();
if (idx && idx != _tabs.Size() - 1)
{
ActionAndArgs action;
action.Action(ShortcutAction::SwitchToTab);
SwitchToTabArgs switchToTabArgs{ idx.value() };
action.Args(switchToTabArgs);
actions.push_back(action);
}
WindowLayout layout{};
layout.TabLayout(winrt::single_threaded_vector<ActionAndArgs>(std::move(actions)));
// Only save the content size because the tab size will be added on load.
const float contentWidth = ::base::saturated_cast<float>(_tabContent.ActualWidth());
const float contentHeight = ::base::saturated_cast<float>(_tabContent.ActualHeight());
const winrt::Windows::Foundation::Size windowSize{ contentWidth, contentHeight };
layout.InitialSize(windowSize);
if (_hostingHwnd)
{
// Get the position of the current window. This includes the
// non-client already.
RECT window{};
GetWindowRect(_hostingHwnd.value(), &window);
// We want to remove the non-client area so calculate that.
// We don't have access to the (NonClient)IslandWindow directly so
// just replicate the logic.
const auto windowStyle = static_cast<DWORD>(GetWindowLong(_hostingHwnd.value(), GWL_STYLE));
auto dpi = GetDpiForWindow(_hostingHwnd.value());
RECT nonClientArea{};
LOG_IF_WIN32_BOOL_FALSE(AdjustWindowRectExForDpi(&nonClientArea, windowStyle, false, 0, dpi));
// The nonClientArea adjustment is negative, so subtract that out.
// This way we save the user-visible location of the terminal.
LaunchPosition pos{};
pos.X = window.left - nonClientArea.left;
pos.Y = window.top;
layout.InitialPosition(pos);
}
auto state = ApplicationState::SharedInstance();
state.PersistedWindowLayouts(winrt::single_threaded_vector<WindowLayout>({ layout }));
}
// Method Description:
// - Close the terminal app. If there is more
// than one tab opened, show a warning dialog.
fire_and_forget TerminalPage::CloseWindow()
// Arguments:
// - bypassDialog: if true a dialog won't be shown even if the user would
// normally get confirmation. This is used in the case where the user
// has already been prompted by the Quit action.
fire_and_forget TerminalPage::CloseWindow(bool bypassDialog)
{
if (_HasMultipleTabs() &&
if (!bypassDialog &&
_HasMultipleTabs() &&
_settings.GlobalSettings().ConfirmCloseAllTabs() &&
!_displayingCloseDialog)
{
@ -1229,6 +1392,13 @@ namespace winrt::TerminalApp::implementation
}
}
if (ShouldUsePersistedLayout(_settings))
{
PersistWindowLayout();
// don't delete the ApplicationState when all of the tabs are removed.
_maintainStateOnTabClose = true;
}
_RemoveAllTabs();
}
@ -2843,11 +3013,14 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Displays a dialog stating the "Touch Keyboard and Handwriting Panel
// Service" is disabled.
void TerminalPage::ShowKeyboardServiceWarning()
void TerminalPage::ShowKeyboardServiceWarning() const
{
if (auto keyboardWarningInfoBar = FindName(L"KeyboardWarningInfoBar").try_as<MUX::Controls::InfoBar>())
if (!_IsMessageDismissed(InfoBarMessage::KeyboardServiceWarning))
{
keyboardWarningInfoBar.IsOpen(true);
if (const auto keyboardServiceWarningInfoBar = FindName(L"KeyboardServiceWarningInfoBar").try_as<MUX::Controls::InfoBar>())
{
keyboardServiceWarningInfoBar.IsOpen(true);
}
}
}
@ -3067,6 +3240,11 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::SetNumberOfOpenWindows(const uint64_t num)
{
_numOpenWindows = num;
}
// Method Description:
// - Returns a label like "Window: 1234" for the ID of this window
// Arguments:
@ -3204,4 +3382,99 @@ namespace winrt::TerminalApp::implementation
}
return profile;
}
// Method Description:
// - Handles the change of connection state.
// If the connection state is failure show information bar suggesting to configure termination behavior
// (unless user asked not to show this message again)
// Arguments:
// - sender: the ICoreState instance containing the connection state
// Return Value:
// - <none>
winrt::fire_and_forget TerminalPage::_ConnectionStateChangedHandler(const IInspectable& sender, const IInspectable& /*args*/) const
{
if (const auto coreState{ sender.try_as<winrt::Microsoft::Terminal::Control::ICoreState>() })
{
const auto newConnectionState = coreState.ConnectionState();
if (newConnectionState == ConnectionState::Failed && !_IsMessageDismissed(InfoBarMessage::CloseOnExitInfo))
{
co_await winrt::resume_foreground(Dispatcher());
if (const auto infoBar = FindName(L"CloseOnExitInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(true);
}
}
}
}
// Method Description:
// - Persists the user's choice not to show information bar guiding to configure termination behavior.
// Then hides this information buffer.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::_CloseOnExitInfoDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) const
{
_DismissMessage(InfoBarMessage::CloseOnExitInfo);
if (const auto infoBar = FindName(L"CloseOnExitInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(false);
}
}
// Method Description:
// - Persists the user's choice not to show information bar warning about "Touch keyboard and Handwriting Panel Service" disabled
// Then hides this information buffer.
// Arguments:
// - <none>
// Return Value:
// - <none>
void TerminalPage::_KeyboardServiceWarningInfoDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) const
{
_DismissMessage(InfoBarMessage::KeyboardServiceWarning);
if (const auto infoBar = FindName(L"KeyboardServiceWarningInfoBar").try_as<MUX::Controls::InfoBar>())
{
infoBar.IsOpen(false);
}
}
// Method Description:
// - Checks whether information bar message was dismissed earlier (in the application state)
// Arguments:
// - message: message to look for in the state
// Return Value:
// - true, if the message was dismissed
bool TerminalPage::_IsMessageDismissed(const InfoBarMessage& message)
{
if (const auto dismissedMessages{ ApplicationState::SharedInstance().DismissedMessages() })
{
for (const auto& dismissedMessage : dismissedMessages)
{
if (dismissedMessage == message)
{
return true;
}
}
}
return false;
}
// Method Description:
// - Persists the user's choice to dismiss information bar message (in application state)
// Arguments:
// - message: message to dismiss
// Return Value:
// - <none>
void TerminalPage::_DismissMessage(const InfoBarMessage& message)
{
auto dismissedMessages = ApplicationState::SharedInstance().DismissedMessages();
if (!dismissedMessages)
{
dismissedMessages = winrt::single_threaded_vector<InfoBarMessage>();
}
dismissedMessages.Append(message);
ApplicationState::SharedInstance().DismissedMessages(dismissedMessages);
}
}

View file

@ -58,6 +58,8 @@ namespace winrt::TerminalApp::implementation
void Create();
bool ShouldUsePersistedLayout(Microsoft::Terminal::Settings::Model::CascadiaSettings& settings) const;
winrt::fire_and_forget NewTerminalByDrop(winrt::Windows::UI::Xaml::DragEventArgs& e);
hstring Title();
@ -69,7 +71,8 @@ namespace winrt::TerminalApp::implementation
winrt::hstring ApplicationDisplayName();
winrt::hstring ApplicationVersion();
winrt::fire_and_forget CloseWindow();
winrt::fire_and_forget RequestQuit();
winrt::fire_and_forget CloseWindow(bool bypassDialog);
void ToggleFocusMode();
void ToggleFullscreen();
@ -79,6 +82,8 @@ namespace winrt::TerminalApp::implementation
bool AlwaysOnTop() const;
void SetStartupActions(std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs>& actions);
void PersistWindowLayout();
void SetInboundListener(bool isEmbedding);
static std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> ConvertExecuteCommandlineToActions(const Microsoft::Terminal::Settings::Model::ExecuteCommandlineArgs& args);
@ -87,7 +92,7 @@ namespace winrt::TerminalApp::implementation
winrt::TerminalApp::TaskbarState TaskbarState() const;
void ShowKeyboardServiceWarning();
void ShowKeyboardServiceWarning() const;
winrt::hstring KeyboardServiceDisabledText();
winrt::fire_and_forget IdentifyWindow();
@ -104,6 +109,9 @@ namespace winrt::TerminalApp::implementation
winrt::fire_and_forget WindowName(const winrt::hstring& value);
uint64_t WindowId() const noexcept;
void WindowId(const uint64_t& value);
void SetNumberOfOpenWindows(const uint64_t value);
winrt::hstring WindowIdForDisplay() const noexcept;
winrt::hstring WindowNameForDisplay() const noexcept;
bool IsQuakeWindow() const noexcept;
@ -124,6 +132,8 @@ namespace winrt::TerminalApp::implementation
TYPED_EVENT(RenameWindowRequested, Windows::Foundation::IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs);
TYPED_EVENT(IsQuakeWindowChanged, IInspectable, IInspectable);
TYPED_EVENT(SummonWindowRequested, IInspectable, IInspectable);
TYPED_EVENT(OpenSystemMenu, IInspectable, IInspectable);
TYPED_EVENT(QuitRequested, IInspectable, IInspectable);
private:
friend struct TerminalPageT<TerminalPage>; // for Xaml to bind events
@ -155,10 +165,12 @@ namespace winrt::TerminalApp::implementation
bool _isAlwaysOnTop{ false };
winrt::hstring _WindowName{};
uint64_t _WindowId{ 0 };
uint64_t _numOpenWindows{ 0 };
bool _rearranging;
std::optional<int> _rearrangeFrom;
std::optional<int> _rearrangeTo;
bool _maintainStateOnTabClose{ false };
bool _rearranging{ false };
std::optional<int> _rearrangeFrom{};
std::optional<int> _rearrangeTo{};
bool _removing{ false };
uint32_t _systemRowsToScroll{ DefaultRowsToScroll };
@ -181,6 +193,7 @@ namespace winrt::TerminalApp::implementation
bool _isElevated() const noexcept;
void _ShowAboutDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowQuitDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseWarningDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowCloseReadOnlyDialog();
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowMultiLinePasteWarningDialog();
@ -368,6 +381,13 @@ namespace winrt::TerminalApp::implementation
const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
void _adminWarningCancelClicked(const winrt::TerminalApp::AdminWarningPlaceholder& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& args);
winrt::fire_and_forget _ConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
void _CloseOnExitInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
void _KeyboardServiceWarningInfoDismissHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args) const;
static bool _IsMessageDismissed(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
static void _DismissMessage(const winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage& message);
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
#define ON_ALL_ACTIONS(action) DECLARE_ACTION_HANDLER(action);

View file

@ -33,6 +33,7 @@ namespace TerminalApp
UInt64 WindowId;
String WindowNameForDisplay { get; };
String WindowIdForDisplay { get; };
void SetNumberOfOpenWindows(UInt64 num);
void RenameFailed();
Boolean IsQuakeWindow();
@ -57,5 +58,6 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> IsQuakeWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
}
}

View file

@ -47,6 +47,11 @@
</StackPanel>
</ContentDialog>
<ContentDialog x:Name="QuitDialog"
x:Uid="QuitDialog"
x:Load="False"
DefaultButton="Primary" />
<ContentDialog x:Name="CloseAllDialog"
x:Uid="CloseAllDialog"
x:Load="False"
@ -114,13 +119,33 @@
PreviewKeyDown="_KeyDownHandler"
Visibility="Collapsed" />
<mux:InfoBar x:Name="KeyboardWarningInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"
Severity="Warning" />
<StackPanel>
<mux:InfoBar x:Name="KeyboardServiceWarningInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Message="{x:Bind KeyboardServiceDisabledText, Mode=OneWay}"
Severity="Warning">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_KeyboardServiceWarningInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
<mux:InfoBar x:Name="CloseOnExitInfoBar"
x:Uid="CloseOnExitInfoBar"
x:Load="False"
IsClosable="True"
IsIconVisible="True"
IsOpen="False"
Severity="Informational">
<mux:InfoBar.ActionButton>
<Button x:Uid="InfoBarDismissButton"
Click="_CloseOnExitInfoDismissHandler" />
</mux:InfoBar.ActionButton>
</mux:InfoBar>
</StackPanel>
<!--
A TeachingTip with IsLightDismissEnabled="True" will immediately

View file

@ -440,6 +440,50 @@ namespace winrt::TerminalApp::implementation
control.ScrollViewport(::base::ClampAdd(currentOffset, delta));
}
// Method Description:
// - Serializes the state of this tab as a series of commands that can be
// executed to recreate it.
// Arguments:
// - <none>
// Return Value:
// - A vector of commands
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions() const
{
// Give initial ids (0 for the child created with this tab,
// 1 for the child after the first split.
auto state = _rootPane->BuildStartupActions(0, 1);
ActionAndArgs newTabAction{};
newTabAction.Action(ShortcutAction::NewTab);
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane() };
newTabAction.Args(newTabArgs);
state.args.emplace(state.args.begin(), std::move(newTabAction));
// If we only have one arg, we only have 1 pane so we don't need any
// special focus logic
if (state.args.size() > 1 && state.focusedPaneId.has_value())
{
ActionAndArgs focusPaneAction{};
focusPaneAction.Action(ShortcutAction::FocusPane);
FocusPaneArgs focusArgs{ state.focusedPaneId.value() };
focusPaneAction.Args(focusArgs);
state.args.emplace_back(std::move(focusPaneAction));
}
if (_zoomedPane)
{
// we start without any panes zoomed so toggle zoom will enable zoom.
ActionAndArgs zoomPaneAction{};
zoomPaneAction.Action(ShortcutAction::TogglePaneZoom);
state.args.emplace_back(std::move(zoomPaneAction));
}
return state.args;
}
// Method Description:
// - Split the focused pane in our tree of panes, and place the
// given TermControl into the newly created pane.

View file

@ -86,6 +86,8 @@ namespace winrt::TerminalApp::implementation
void EnterZoom();
void ExitZoom();
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions() const;
int GetLeafPaneCount() const noexcept;
void TogglePaneReadOnly();

View file

@ -2,5 +2,5 @@
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
<package id="vcpkg-cpprestsdk" version="2.10.14" targetFramework="native" />
</packages>

View file

@ -16,18 +16,23 @@
<ResourceDictionary>
<Style x:Key="ToggleButtonStyle"
TargetType="ToggleButton">
<Setter Property="Width" Value="25" />
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="1" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="2,0" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
</Style>
<Style x:Key="ButtonStyle"
TargetType="Button">
<Setter Property="Width" Value="25" />
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="24" />
<Setter Property="Height" Value="24" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="6,0,0,0" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
</Style>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
@ -227,14 +232,15 @@
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Padding="5"
CornerRadius="0,0,2,2"
<StackPanel Margin="8"
Padding="8"
CornerRadius="{ThemeResource OverlayCornerRadius}"
Orientation="Horizontal"
Style="{ThemeResource SearchBoxBackground}">
<TextBox x:Name="TextBox"
x:Uid="SearchBox_TextBox"
Width="160"
Margin="5"
Margin="0,0,6,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
CornerRadius="2"
@ -270,7 +276,6 @@
<Button x:Name="CloseButton"
x:Uid="SearchBox_Close"
Padding="0"
Click="CloseClick"
Style="{ThemeResource ButtonStyle}">
<FontIcon FontFamily="Segoe MDL2 Assets"

View file

@ -904,24 +904,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
if (vkey == VK_SPACE && modifiers.IsAltPressed())
{
if (const auto bindings = _settings.KeyBindings())
{
if (!bindings.IsKeyChordExplicitlyUnbound({ modifiers.IsCtrlPressed(), modifiers.IsAltPressed(), modifiers.IsShiftPressed(), modifiers.IsWinPressed(), vkey, scanCode }))
{
// If we get here, it means that
// 1. we do not have a command bound to alt+space
// 2. alt+space was not explicitly unbound
// That means that XAML handled the alt+space to open up the context menu, and
// so we don't want to send anything to the terminal
// TODO GH#11018: Add a new "openSystemMenu" keybinding
e.Handled(true);
return;
}
}
}
if (_TrySendKeyEvent(vkey, scanCode, modifiers, keyDown))
{
e.Handled(true);

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -17,6 +17,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
InitializeComponent();
INITIALIZE_BINDABLE_ENUM_SETTING(FirstWindowPreference, FirstWindowPreference, FirstWindowPreference, L"Globals_FirstWindowPreference", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(LaunchMode, LaunchMode, LaunchMode, L"Globals_LaunchMode", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(WindowingBehavior, WindowingMode, WindowingMode, L"Globals_WindowingBehavior", L"Content");
@ -68,4 +69,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
return winrt::single_threaded_observable_vector(std::move(profiles));
}
bool Launch::ShowFirstWindowPreference() const noexcept
{
return Feature_PersistedWindowLayout::IsEnabled();
}
}

View file

@ -29,8 +29,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void CurrentDefaultProfile(const IInspectable& value);
winrt::Windows::Foundation::Collections::IObservableVector<IInspectable> DefaultProfiles() const;
bool ShowFirstWindowPreference() const noexcept;
WINRT_PROPERTY(Editor::LaunchPageNavigationState, State, nullptr);
GETSET_BINDABLE_ENUM_SETTING(FirstWindowPreference, Model::FirstWindowPreference, State().Settings().GlobalSettings, FirstWindowPreference);
GETSET_BINDABLE_ENUM_SETTING(LaunchMode, Model::LaunchMode, State().Settings().GlobalSettings, LaunchMode);
GETSET_BINDABLE_ENUM_SETTING(WindowingBehavior, Model::WindowingMode, State().Settings().GlobalSettings, WindowingBehavior);
};

View file

@ -20,6 +20,12 @@ namespace Microsoft.Terminal.Settings.Editor
// https://github.com/microsoft/microsoft-ui-xaml/issues/5395
IObservableVector<IInspectable> DefaultProfiles { get; };
Boolean ShowFirstWindowPreference { get; };
IInspectable CurrentFirstWindowPreference;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> FirstWindowPreferenceList { get; };
IInspectable CurrentLaunchMode;
IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> LaunchModeList { get; };

View file

@ -133,6 +133,15 @@
<ToggleSwitch IsOn="{x:Bind State.Settings.GlobalSettings.StartOnUserLogin, Mode=TwoWay}" />
</local:SettingContainer>
<!-- First Window Behavior -->
<local:SettingContainer x:Uid="Globals_FirstWindowPreference"
Visibility="{x:Bind ShowFirstWindowPreference}">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}"
ItemsSource="{x:Bind FirstWindowPreferenceList}"
SelectedItem="{x:Bind CurrentFirstWindowPreference, Mode=TwoWay}" />
</local:SettingContainer>
<!-- Launch Mode -->
<local:SettingContainer x:Uid="Globals_LaunchMode">
<muxc:RadioButtons ItemTemplate="{StaticResource EnumRadioButtonTemplate}"

View file

@ -291,6 +291,22 @@
<value>The number of rows displayed in the window upon first load. Measured in characters.</value>
<comment>A description for what the "rows" setting does. Presented near "Globals_InitialRows.Header".</comment>
</data>
<data name="Globals_FirstWindowPreference.Header" xml:space="preserve">
<value>When Terminal starts</value>
<comment>Header for a control to select how the terminal should load its first window.</comment>
</data>
<data name="Globals_FirstWindowPreference.HelpText" xml:space="preserve">
<value>What should be shown when the first terminal is created.</value>
<comment></comment>
</data>
<data name="Globals_FirstWindowPreferenceDefaultProfile.Content" xml:space="preserve">
<value>Open a tab with the default profile</value>
<comment>An option to choose from for the "First window preference" setting. Open the default profile.</comment>
</data>
<data name="Globals_FirstWindowPreferencePersistedWindowLayout.Content" xml:space="preserve">
<value>Open tabs from a previous session</value>
<comment>An option to choose from for the "First window preference" setting. Reopen the layout from the last session.</comment>
</data>
<data name="Globals_LaunchMode.Header" xml:space="preserve">
<value>Launch mode</value>
<comment>Header for a control to select what mode to launch the terminal in.</comment>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -65,8 +65,10 @@ static constexpr std::string_view OpenWindowRenamerKey{ "openWindowRenamer" };
static constexpr std::string_view GlobalSummonKey{ "globalSummon" };
static constexpr std::string_view QuakeModeKey{ "quakeMode" };
static constexpr std::string_view FocusPaneKey{ "focusPane" };
static constexpr std::string_view OpenSystemMenuKey{ "openSystemMenu" };
static constexpr std::string_view ClearBufferKey{ "clearBuffer" };
static constexpr std::string_view MultipleActionsKey{ "multipleActions" };
static constexpr std::string_view QuitKey{ "quit" };
static constexpr std::string_view ActionKey{ "action" };
@ -368,8 +370,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::GlobalSummon, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::QuakeMode, RS_(L"QuakeModeCommandKey") },
{ ShortcutAction::FocusPane, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::OpenSystemMenu, RS_(L"OpenSystemMenuCommandKey") },
{ ShortcutAction::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
{ ShortcutAction::Quit, RS_(L"QuitCommandKey") },
};
}();

View file

@ -126,14 +126,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
bool Equals(const Model::NewTerminalArgs& other)
{
return other.Commandline() == _Commandline &&
other.StartingDirectory() == _StartingDirectory &&
other.TabTitle() == _TabTitle &&
other.TabColor() == _TabColor &&
other.ProfileIndex() == _ProfileIndex &&
other.Profile() == _Profile &&
other.SuppressApplicationTitle() == _SuppressApplicationTitle &&
other.ColorScheme() == _ColorScheme;
auto otherAsUs = other.try_as<NewTerminalArgs>();
if (otherAsUs)
{
return otherAsUs->_Commandline == _Commandline &&
otherAsUs->_StartingDirectory == _StartingDirectory &&
otherAsUs->_TabTitle == _TabTitle &&
otherAsUs->_TabColor == _TabColor &&
otherAsUs->_ProfileIndex == _ProfileIndex &&
otherAsUs->_Profile == _Profile &&
otherAsUs->_SuppressApplicationTitle == _SuppressApplicationTitle &&
otherAsUs->_ColorScheme == _ColorScheme;
}
return false;
};
static Model::NewTerminalArgs FromJson(const Json::Value& json)
{

View file

@ -79,8 +79,10 @@
ON_ALL_ACTIONS(GlobalSummon) \
ON_ALL_ACTIONS(QuakeMode) \
ON_ALL_ACTIONS(FocusPane) \
ON_ALL_ACTIONS(OpenSystemMenu) \
ON_ALL_ACTIONS(ClearBuffer) \
ON_ALL_ACTIONS(MultipleActions)
ON_ALL_ACTIONS(MultipleActions) \
ON_ALL_ACTIONS(Quit)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \

View file

@ -5,11 +5,56 @@
#include "ApplicationState.h"
#include "CascadiaSettings.h"
#include "ApplicationState.g.cpp"
#include "WindowLayout.g.cpp"
#include "ActionAndArgs.h"
#include "JsonUtils.h"
#include "FileUtils.h"
constexpr std::wstring_view stateFileName{ L"state.json" };
static constexpr std::wstring_view stateFileName{ L"state.json" };
static constexpr std::string_view TabLayoutKey{ "tabLayout" };
static constexpr std::string_view InitialPositionKey{ "initialPosition" };
static constexpr std::string_view InitialSizeKey{ "initialSize" };
namespace Microsoft::Terminal::Settings::Model::JsonUtils
{
using namespace winrt::Microsoft::Terminal::Settings::Model;
template<>
struct ConversionTrait<WindowLayout>
{
WindowLayout FromJson(const Json::Value& json)
{
auto layout = winrt::make_self<implementation::WindowLayout>();
GetValueForKey(json, TabLayoutKey, layout->_TabLayout);
GetValueForKey(json, InitialPositionKey, layout->_InitialPosition);
GetValueForKey(json, InitialSizeKey, layout->_InitialSize);
return *layout;
}
bool CanConvert(const Json::Value& json)
{
return json.isObject();
}
Json::Value ToJson(const WindowLayout& val)
{
Json::Value json{ Json::objectValue };
SetValueForKey(json, TabLayoutKey, val.TabLayout());
SetValueForKey(json, InitialPositionKey, val.InitialPosition());
SetValueForKey(json, InitialSizeKey, val.InitialSize());
return json;
}
std::string TypeDescription() const
{
return "WindowLayout";
}
};
}
using namespace ::Microsoft::Terminal::Settings::Model;

View file

@ -14,17 +14,31 @@ Abstract:
#include "BaseApplicationState.h"
#include "ApplicationState.g.h"
#include "WindowLayout.g.h"
#include <inc/cppwinrt_utils.h>
#include <JsonUtils.h>
// This macro generates all getters and setters for ApplicationState.
// It provides X with the following arguments:
// (type, function name, JSON key, ...variadic construction arguments)
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands")
namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
#define MTSM_APPLICATION_STATE_FIELDS(X) \
X(std::unordered_set<winrt::guid>, GeneratedProfiles, "generatedProfiles") \
X(Windows::Foundation::Collections::IVector<Model::WindowLayout>, PersistedWindowLayouts, "persistedWindowLayouts") \
X(Windows::Foundation::Collections::IVector<hstring>, RecentCommands, "recentCommands") \
X(Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage>, DismissedMessages, "dismissedMessages")
struct WindowLayout : WindowLayoutT<WindowLayout>
{
WINRT_PROPERTY(Windows::Foundation::Collections::IVector<Model::ActionAndArgs>, TabLayout, nullptr);
WINRT_PROPERTY(winrt::Windows::Foundation::IReference<Model::LaunchPosition>, InitialPosition, nullptr);
WINRT_PROPERTY(winrt::Windows::Foundation::IReference<winrt::Windows::Foundation::Size>, InitialSize, nullptr);
friend ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<Model::WindowLayout>;
};
struct ApplicationState : public BaseApplicationState, ApplicationStateT<ApplicationState>
{
static Microsoft::Terminal::Settings::Model::ApplicationState SharedInstance();
@ -54,5 +68,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
{
BASIC_FACTORY(WindowLayout)
BASIC_FACTORY(ApplicationState);
}

View file

@ -1,8 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "Command.idl";
import "GlobalAppSettings.idl";
namespace Microsoft.Terminal.Settings.Model
{
enum InfoBarMessage
{
CloseOnExitInfo = 0,
KeyboardServiceWarning
};
runtimeclass WindowLayout
{
WindowLayout();
Windows.Foundation.Collections.IVector<ActionAndArgs> TabLayout;
Windows.Foundation.IReference<LaunchPosition> InitialPosition;
Windows.Foundation.IReference<Windows.Foundation.Size> InitialSize;
};
[default_interface] runtimeclass ApplicationState {
static ApplicationState SharedInstance();
@ -10,6 +27,10 @@ namespace Microsoft.Terminal.Settings.Model
String FilePath { get; };
Windows.Foundation.Collections.IVector<WindowLayout> PersistedWindowLayouts { get; set; };
Windows.Foundation.Collections.IVector<String> RecentCommands { get; set; };
Windows.Foundation.Collections.IVector<InfoBarMessage> DismissedMessages { get; set; };
}
}

View file

@ -4,6 +4,7 @@
namespace Microsoft.Terminal.Settings.Model
{
[default_interface] runtimeclass ColorScheme : Windows.Foundation.IStringable {
ColorScheme();
ColorScheme(String name);
String Name;

View file

@ -32,6 +32,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// Global Settings
DEFINE_ENUM_MAP(winrt::Windows::UI::Xaml::ElementTheme, ElementTheme);
DEFINE_ENUM_MAP(winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabViewWidthMode);
DEFINE_ENUM_MAP(Model::FirstWindowPreference, FirstWindowPreference);
DEFINE_ENUM_MAP(Model::LaunchMode, LaunchMode);
DEFINE_ENUM_MAP(Model::TabSwitcherMode, TabSwitcherMode);
DEFINE_ENUM_MAP(Microsoft::Terminal::Control::CopyFormat, CopyFormat);

View file

@ -28,6 +28,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// Global Settings
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Windows::UI::Xaml::ElementTheme> ElementTheme();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode> TabViewWidthMode();
static winrt::Windows::Foundation::Collections::IMap<winrt::hstring, FirstWindowPreference> FirstWindowPreference();
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::Control::CopyFormat> CopyFormat();

View file

@ -10,6 +10,7 @@ namespace Microsoft.Terminal.Settings.Model
// Global Settings
static Windows.Foundation.Collections.IMap<String, Windows.UI.Xaml.ElementTheme> ElementTheme { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.UI.Xaml.Controls.TabViewWidthMode> TabViewWidthMode { get; };
static Windows.Foundation.Collections.IMap<String, Microsoft.Terminal.Settings.Model.FirstWindowPreference> FirstWindowPreference { get; };
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.Control.CopyFormat> CopyFormat { get; };

View file

@ -41,6 +41,7 @@ static constexpr std::string_view LaunchModeKey{ "launchMode" };
static constexpr std::string_view ConfirmCloseAllKey{ "confirmCloseAllTabs" };
static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" };
static constexpr std::string_view EnableStartupTaskKey{ "startOnUserLogin" };
static constexpr std::string_view FirstWindowPreferenceKey{ "firstWindowPreference" };
static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" };
static constexpr std::string_view LegacyUseTabSwitcherModeKey{ "useTabSwitcher" };
static constexpr std::string_view TabSwitcherModeKey{ "tabSwitcherMode" };
@ -125,6 +126,7 @@ winrt::com_ptr<GlobalAppSettings> GlobalAppSettings::Copy() const
globals->_ForceVTInput = _ForceVTInput;
globals->_DebugFeaturesEnabled = _DebugFeaturesEnabled;
globals->_StartOnUserLogin = _StartOnUserLogin;
globals->_FirstWindowPreference = _FirstWindowPreference;
globals->_AlwaysOnTop = _AlwaysOnTop;
globals->_TabSwitcherMode = _TabSwitcherMode;
globals->_DisableAnimations = _DisableAnimations;
@ -285,6 +287,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)
JsonUtils::GetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
JsonUtils::GetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference);
JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode);
JsonUtils::GetValueForKey(json, LanguageKey, _Language);
@ -408,6 +412,7 @@ Json::Value GlobalAppSettings::ToJson() const
JsonUtils::SetValueForKey(json, CopyFormattingKey, _CopyFormatting);
JsonUtils::SetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste);
JsonUtils::SetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste);
JsonUtils::SetValueForKey(json, FirstWindowPreferenceKey, _FirstWindowPreference);
JsonUtils::SetValueForKey(json, LaunchModeKey, _LaunchMode);
JsonUtils::SetValueForKey(json, LanguageKey, _Language);
JsonUtils::SetValueForKey(json, ThemeKey, _Theme);

View file

@ -84,6 +84,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, WarnAboutMultiLinePaste, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::LaunchPosition, InitialPosition, nullptr, nullptr);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, CenterOnLaunch, false);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::FirstWindowPreference, FirstWindowPreference, FirstWindowPreference::DefaultProfile);
INHERITABLE_SETTING(Model::GlobalAppSettings, Model::LaunchMode, LaunchMode, LaunchMode::DefaultMode);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, SnapToGridOnResize, true);
INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ForceFullRepaintRendering, false);

View file

@ -34,6 +34,12 @@ namespace Microsoft.Terminal.Settings.Model
UseExisting,
};
enum FirstWindowPreference
{
DefaultProfile,
PersistedWindowLayout,
};
[default_interface] runtimeclass GlobalAppSettings {
Guid DefaultProfile;
@ -59,6 +65,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_SETTING(Boolean, WarnAboutMultiLinePaste);
INHERITABLE_SETTING(LaunchPosition, InitialPosition);
INHERITABLE_SETTING(Boolean, CenterOnLaunch);
INHERITABLE_SETTING(FirstWindowPreference, FirstWindowPreference);
INHERITABLE_SETTING(LaunchMode, LaunchMode);
INHERITABLE_SETTING(Boolean, SnapToGridOnResize);
INHERITABLE_SETTING(Boolean, ForceFullRepaintRendering);

View file

@ -117,6 +117,127 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
std::string expectedType;
};
// Method Description:
// - Helper that will populate a reference with a value converted from a json object.
// Arguments:
// - json: the json object to convert
// - target: the value to populate with the converted result
// Return Value:
// - a boolean indicating whether the value existed (in this case, was non-null)
//
// GetValue, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
{
if (!conv.CanConvert(json))
{
DeserializationError e{ json };
e.expectedType = conv.TypeDescription();
throw e;
}
target = conv.FromJson(json);
return true;
}
// GetValue, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
{
std::decay_t<T> local{};
GetValue(json, local, std::forward<Converter>(conv));
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
{
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
{
try
{
return GetValue(*found, target, std::forward<Converter>(conv));
}
catch (DeserializationError& e)
{
e.SetKey(key);
throw; // rethrow now that it has a key
}
}
return false;
}
// GetValueForKey, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
{
std::decay_t<T> local{};
GetValueForKey(json, key, local, std::forward<Converter>(conv));
return local; // returns zero-initialized?
}
// GetValue, type-deduced, with automatic converter
template<typename T>
bool GetValue(const Json::Value& json, T& target)
{
return GetValue(json, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValue, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValue(const Json::Value& json)
{
std::decay_t<T> local{};
GetValue(json, local, ConversionTrait<typename std::decay<T>::type>{});
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, with automatic converter
template<typename T>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
{
return GetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValueForKey, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
{
return GetValueForKey<T>(json, key, ConversionTrait<typename std::decay<T>::type>{});
}
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
// Uses the default converter for each v.
// Careful: this can cause a template explosion.
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
template<typename T, typename... Args>
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
{
GetValueForKey(json, key1, val1);
GetValuesForKeys(json, std::forward<Args>(args)...);
}
// SetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target, Converter&& conv)
{
// We don't want to write any empty optionals into JSON (right now).
if (OptionOracle<T>::HasValue(target))
{
// demand guarantees that it will return a value or throw an exception
*json.demand(&*key.cbegin(), (&*key.cbegin()) + key.size()) = conv.ToJson(target);
}
}
// SetValueForKey, type-deduced, with automatic converter
template<typename T>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target)
{
SetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
template<typename T>
struct ConversionTrait
{
@ -238,7 +359,7 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
bool CanConvert(const Json::Value& json) const
{
ConversionTrait<T> trait;
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) -> bool { return trait.CanConvert(json); });
return json.isArray() && std::all_of(json.begin(), json.end(), [trait](const auto& json) mutable -> bool { return trait.CanConvert(json); });
}
Json::Value ToJson(const std::unordered_set<T>& val)
@ -637,6 +758,46 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
}
};
#ifdef WINRT_Windows_Foundation_H
template<>
struct ConversionTrait<winrt::Windows::Foundation::Size>
{
winrt::Windows::Foundation::Size FromJson(const Json::Value& json)
{
winrt::Windows::Foundation::Size size{};
GetValueForKey(json, std::string_view("width"), size.Width);
GetValueForKey(json, std::string_view("height"), size.Height);
return size;
}
bool CanConvert(const Json::Value& json)
{
if (!json.isObject())
{
return false;
}
return json.isMember("width") && json.isMember("height");
}
Json::Value ToJson(const winrt::Windows::Foundation::Size& val)
{
Json::Value json{ Json::objectValue };
SetValueForKey(json, std::string_view("width"), val.Width);
SetValueForKey(json, std::string_view("height"), val.Height);
return json;
}
std::string TypeDescription() const
{
return "size { width, height }";
}
};
#endif
#ifdef WINRT_Windows_UI_H
template<>
struct ConversionTrait<winrt::Windows::UI::Color>
@ -894,127 +1055,6 @@ namespace Microsoft::Terminal::Settings::Model::JsonUtils
return "any";
}
};
// Method Description:
// - Helper that will populate a reference with a value converted from a json object.
// Arguments:
// - json: the json object to convert
// - target: the value to populate with the converted result
// Return Value:
// - a boolean indicating whether the value existed (in this case, was non-null)
//
// GetValue, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValue(const Json::Value& json, T& target, Converter&& conv)
{
if (!conv.CanConvert(json))
{
DeserializationError e{ json };
e.expectedType = conv.TypeDescription();
throw e;
}
target = conv.FromJson(json);
return true;
}
// GetValue, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValue(const Json::Value& json, Converter&& conv)
{
std::decay_t<T> local{};
GetValue(json, local, std::forward<Converter>(conv));
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target, Converter&& conv)
{
if (auto found{ json.find(&*key.cbegin(), (&*key.cbegin()) + key.size()) })
{
try
{
return GetValue(*found, target, std::forward<Converter>(conv));
}
catch (DeserializationError& e)
{
e.SetKey(key);
throw; // rethrow now that it has a key
}
}
return false;
}
// GetValueForKey, forced return type, manual converter
template<typename T, typename Converter>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key, Converter&& conv)
{
std::decay_t<T> local{};
GetValueForKey(json, key, local, std::forward<Converter>(conv));
return local; // returns zero-initialized?
}
// GetValue, type-deduced, with automatic converter
template<typename T>
bool GetValue(const Json::Value& json, T& target)
{
return GetValue(json, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValue, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValue(const Json::Value& json)
{
std::decay_t<T> local{};
GetValue(json, local, ConversionTrait<typename std::decay<T>::type>{});
return local; // returns zero-initialized or value
}
// GetValueForKey, type-deduced, with automatic converter
template<typename T>
bool GetValueForKey(const Json::Value& json, std::string_view key, T& target)
{
return GetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
// GetValueForKey, forced return type, with automatic converter
template<typename T>
std::decay_t<T> GetValueForKey(const Json::Value& json, std::string_view key)
{
return GetValueForKey<T>(json, key, ConversionTrait<typename std::decay<T>::type>{});
}
// Get multiple values for keys (json, k, &v, k, &v, k, &v, ...).
// Uses the default converter for each v.
// Careful: this can cause a template explosion.
constexpr void GetValuesForKeys(const Json::Value& /*json*/) {}
template<typename T, typename... Args>
void GetValuesForKeys(const Json::Value& json, std::string_view key1, T&& val1, Args&&... args)
{
GetValueForKey(json, key1, val1);
GetValuesForKeys(json, std::forward<Args>(args)...);
}
// SetValueForKey, type-deduced, manual converter
template<typename T, typename Converter>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target, Converter&& conv)
{
// We don't want to write any empty optionals into JSON (right now).
if (OptionOracle<T>::HasValue(target))
{
// demand guarantees that it will return a value or throw an exception
*json.demand(&*key.cbegin(), (&*key.cbegin()) + key.size()) = conv.ToJson(target);
}
}
// SetValueForKey, type-deduced, with automatic converter
template<typename T>
void SetValueForKey(Json::Value& json, std::string_view key, const T& target)
{
SetValueForKey(json, key, target, ConversionTrait<typename std::decay<T>::type>{});
}
};
#define JSON_ENUM_MAPPER(...) \

View file

@ -178,6 +178,9 @@
<data name="CloseWindowCommandKey" xml:space="preserve">
<value>Close window</value>
</data>
<data name="OpenSystemMenuCommandKey" xml:space="preserve">
<value>Open system menu</value>
</data>
<data name="CommandPromptDisplayName" xml:space="preserve">
<value>Command Prompt</value>
<comment>This is the name of "Command Prompt", as localized in Windows. The localization here should match the one in the Windows product for "Command Prompt"</comment>
@ -469,4 +472,7 @@
<data name="MinimizeToTrayCommandKey" xml:space="preserve">
<value>Minimize current window to tray</value>
</data>
<data name="QuitCommandKey" xml:space="preserve">
<value>Quit the Terminal</value>
</data>
</root>

View file

@ -340,6 +340,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// settings.
if (scheme == nullptr)
{
ClearAppliedColorScheme();
ClearDefaultForeground();
ClearDefaultBackground();
ClearSelectionBackground();
@ -348,6 +349,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
}
else
{
AppliedColorScheme(scheme);
_DefaultForeground = til::color{ scheme.Foreground() };
_DefaultBackground = til::color{ scheme.Background() };
_SelectionBackground = til::color{ scheme.SelectionBackground() };

View file

@ -127,6 +127,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
INHERITABLE_SETTING(Model::TerminalSettings, IFontAxesMap, FontAxes);
INHERITABLE_SETTING(Model::TerminalSettings, IFontFeatureMap, FontFeatures);
INHERITABLE_SETTING(Model::TerminalSettings, Model::ColorScheme, AppliedColorScheme);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, BackgroundImage);
INHERITABLE_SETTING(Model::TerminalSettings, double, BackgroundImageOpacity, 1.0);

View file

@ -33,5 +33,7 @@ namespace Microsoft.Terminal.Settings.Model
void SetParent(TerminalSettings parent);
TerminalSettings GetParent();
void ApplyColorScheme(ColorScheme scheme);
ColorScheme AppliedColorScheme;
};
}

View file

@ -209,6 +209,14 @@ JSON_ENUM_MAPPER(::winrt::Windows::UI::Xaml::ElementTheme)
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FirstWindowPreference)
{
JSON_MAPPINGS(2) = {
pair_type{ "defaultProfile", ValueType::DefaultProfile },
pair_type{ "persistedWindowLayout", ValueType::PersistedWindowLayout },
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::LaunchMode)
{
JSON_MAPPINGS(5) = {
@ -373,7 +381,8 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SplitState)
// Possible SplitType values
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SplitType)
{
JSON_MAPPINGS(1) = {
JSON_MAPPINGS(2) = {
pair_type{ "manual", ValueType::Manual },
pair_type{ "duplicate", ValueType::Duplicate },
};
};
@ -491,3 +500,11 @@ JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::IntenseStyle)
};
};
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::InfoBarMessage)
{
JSON_MAPPINGS(2) = {
pair_type{ "closeOnExitInfo", ValueType::CloseOnExitInfo },
pair_type{ "keyboardServiceWarning", ValueType::KeyboardServiceWarning },
};
};

View file

@ -301,6 +301,8 @@
{ "command": "identifyWindow" },
{ "command": "openWindowRenamer" },
{ "command": "quakeMode", "keys":"win+sc(41)" },
{ "command": "openSystemMenu", "keys": "alt+space" },
{ "command": "quit" },
// Tab Management
// "command": "closeTab" is unbound by default.

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -75,6 +75,8 @@ namespace RemotingUnitTests
void Summon(const Remoting::SummonWindowBehavior& /*args*/) { throw winrt::hresult_error{}; };
void RequestShowTrayIcon() { throw winrt::hresult_error{}; };
void RequestHideTrayIcon() { throw winrt::hresult_error{}; };
void RequestQuitAll() { throw winrt::hresult_error{}; };
void Quit() { throw winrt::hresult_error{}; };
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs);
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs);
TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
@ -83,6 +85,8 @@ namespace RemotingUnitTests
TYPED_EVENT(SummonRequested, winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior);
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(HideTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(QuitRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
};
class RemotingTests

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -213,7 +213,15 @@ void AppHost::_HandleCommandlineArgs()
peasant.SummonRequested({ this, &AppHost::_HandleSummon });
peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId });
peasant.QuitRequested({ this, &AppHost::_QuitRequested });
// We need this property to be set before we get the InitialSize/Position
// and BecameMonarch which normally sets it is only run after the window
// is created.
if (_windowManager.IsMonarch())
{
_logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants());
}
_logic.WindowName(peasant.WindowName());
_logic.WindowId(peasant.GetID());
}
@ -271,6 +279,8 @@ void AppHost::Initialize()
_logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged });
_logic.IsQuakeWindowChanged({ this, &AppHost::_IsQuakeWindowChanged });
_logic.SummonWindowRequested({ this, &AppHost::_SummonWindowRequested });
_logic.OpenSystemMenu({ this, &AppHost::_OpenSystemMenu });
_logic.QuitRequested({ this, &AppHost::_RequestQuitAll });
_window->UpdateTitle(_logic.Title());
@ -673,9 +683,19 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s
_CreateTrayIcon();
}
// Set the number of open windows (so we know if we are the last window)
// and subscribe for updates if there are any changes to that number.
_logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants());
_windowManager.WindowCreated([this](auto&&, auto&&) { _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); });
_windowManager.WindowClosed([this](auto&&, auto&&) { _logic.SetNumberOfOpenWindows(_windowManager.GetNumberOfPeasants()); });
// These events are coming from peasants that become or un-become quake windows.
_windowManager.ShowTrayIconRequested([this](auto&&, auto&&) { _ShowTrayIconRequested(); });
_windowManager.HideTrayIconRequested([this](auto&&, auto&&) { _HideTrayIconRequested(); });
// If the monarch receives a QuitAll event it will signal this event to be
// ran before each peasant is closed.
_windowManager.QuitAllRequested({ this, &AppHost::_QuitAllRequested });
}
void AppHost::_listenForInboundConnections()
@ -1010,6 +1030,28 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab
_window->IsQuakeWindow(_logic.IsQuakeWindow());
}
winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
// Need to be on the main thread to close out all of the tabs.
co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher());
_logic.Quit();
}
void AppHost::_RequestQuitAll(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_windowManager.RequestQuitAll();
}
void AppHost::_QuitAllRequested(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
// TODO: GH#9800: For now, nothing needs to be done before the monarch closes all windows.
// Later when we have state saving that should go here.
}
void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable&)
{
@ -1021,6 +1063,12 @@ void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspecta
_HandleSummon(sender, summonArgs);
}
void AppHost::_OpenSystemMenu(const winrt::Windows::Foundation::IInspectable&,
const winrt::Windows::Foundation::IInspectable&)
{
_window->OpenSystemMenu(std::nullopt, std::nullopt);
}
// Method Description:
// - Creates a Tray Icon and hooks up its handlers
// Arguments:

View file

@ -85,6 +85,18 @@ private:
void _SummonWindowRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _OpenSystemMenu(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
winrt::fire_and_forget _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _RequestQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _QuitAllRequested(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args);
void _CreateTrayIcon();
void _DestroyTrayIcon();
void _ShowTrayIconRequested();

View file

@ -1645,5 +1645,63 @@ void IslandWindow::SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept
_minimizeToTray = minimizeToTray;
}
// Method Description:
// - Opens the window's system menu.
// - The system menu is the menu that opens when the user presses Alt+Space or
// right clicks on the title bar.
// - Before updating the menu, we update the buttons like "Maximize" and
// "Restore" so that they are grayed out depending on the window's state.
// Arguments:
// - cursorX: the cursor's X position in screen coordinates
// - cursorY: the cursor's Y position in screen coordinates
void IslandWindow::OpenSystemMenu(const std::optional<int> mouseX, const std::optional<int> mouseY) const noexcept
{
const auto systemMenu = GetSystemMenu(_window.get(), FALSE);
WINDOWPLACEMENT placement;
if (!GetWindowPlacement(_window.get(), &placement))
{
return;
}
const bool isMaximized = placement.showCmd == SW_SHOWMAXIMIZED;
// Update the options based on window state.
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STATE;
mii.fType = MFT_STRING;
auto setState = [&](UINT item, bool enabled) {
mii.fState = enabled ? MF_ENABLED : MF_DISABLED;
SetMenuItemInfo(systemMenu, item, FALSE, &mii);
};
setState(SC_RESTORE, isMaximized);
setState(SC_MOVE, !isMaximized);
setState(SC_SIZE, !isMaximized);
setState(SC_MINIMIZE, true);
setState(SC_MAXIMIZE, !isMaximized);
setState(SC_CLOSE, true);
SetMenuDefaultItem(systemMenu, UINT_MAX, FALSE);
int xPos;
int yPos;
if (mouseX && mouseY)
{
xPos = mouseX.value();
yPos = mouseY.value();
}
else
{
RECT windowPos;
::GetWindowRect(GetHandle(), &windowPos);
xPos = windowPos.left;
yPos = windowPos.top;
}
const auto ret = TrackPopupMenu(systemMenu, TPM_RETURNCMD, xPos, yPos, 0, _window.get(), nullptr);
if (ret != 0)
{
PostMessage(_window.get(), WM_SYSCOMMAND, ret, 0);
}
}
DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);

View file

@ -51,6 +51,8 @@ public:
void SetMinimizeToTrayBehavior(bool minimizeToTray) noexcept;
void OpenSystemMenu(const std::optional<int> mouseX, const std::optional<int> mouseY) const noexcept;
DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);

View file

@ -750,7 +750,7 @@ void NonClientIslandWindow::_UpdateFrameMargins() const noexcept
// reason so we have to do it ourselves.
if (wParam == HTCAPTION)
{
_OpenSystemMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
OpenSystemMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
}
break;
}
@ -947,47 +947,3 @@ bool NonClientIslandWindow::_IsTitlebarVisible() const
{
return !(_fullscreen || _borderless);
}
// Method Description:
// - Opens the window's system menu.
// - The system menu is the menu that opens when the user presses Alt+Space or
// right clicks on the title bar.
// - Before updating the menu, we update the buttons like "Maximize" and
// "Restore" so that they are grayed out depending on the window's state.
// Arguments:
// - cursorX: the cursor's X position in screen coordinates
// - cursorY: the cursor's Y position in screen coordinates
void NonClientIslandWindow::_OpenSystemMenu(const int cursorX, const int cursorY) const noexcept
{
const auto systemMenu = GetSystemMenu(_window.get(), FALSE);
WINDOWPLACEMENT placement;
if (!GetWindowPlacement(_window.get(), &placement))
{
return;
}
const bool isMaximized = placement.showCmd == SW_SHOWMAXIMIZED;
// Update the options based on window state.
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STATE;
mii.fType = MFT_STRING;
auto setState = [&](UINT item, bool enabled) {
mii.fState = enabled ? MF_ENABLED : MF_DISABLED;
SetMenuItemInfo(systemMenu, item, FALSE, &mii);
};
setState(SC_RESTORE, isMaximized);
setState(SC_MOVE, !isMaximized);
setState(SC_SIZE, !isMaximized);
setState(SC_MINIMIZE, true);
setState(SC_MAXIMIZE, !isMaximized);
setState(SC_CLOSE, true);
SetMenuDefaultItem(systemMenu, UINT_MAX, FALSE);
const auto ret = TrackPopupMenu(systemMenu, TPM_RETURNCMD, cursorX, cursorY, 0, _window.get(), nullptr);
if (ret != 0)
{
PostMessage(_window.get(), WM_SYSCOMMAND, ret, 0);
}
}

View file

@ -87,6 +87,4 @@ private:
void _UpdateFrameMargins() const noexcept;
void _UpdateMaximizedState();
void _UpdateIslandPosition(const UINT windowWidth, const UINT windowHeight);
void _OpenSystemMenu(const int mouseX, const int mouseY) const noexcept;
};

View file

@ -184,16 +184,13 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
}
}
// GH#7125 = System XAML will show a system dialog on Alt Space. We might want to
// explicitly prevent that - for example when an action is bound to it. So similar to
// above, we steal the event and hand it off to the host. When the host does not process
// it, we will still dispatch like normal.
// GH#7125 = System XAML will show a system dialog on Alt Space. We want to
// explicitly prevent that because we handle that ourselves. So similar to
// above, we steal the event and hand it off to the host.
if (_messageIsAltSpaceKeypress(message))
{
if (host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true))
{
continue;
}
host.OnDirectKeyEvent(VK_SPACE, LOBYTE(HIWORD(message.lParam)), true);
continue;
}
TranslateMessage(&message);

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.201202003" targetFramework="native" />
<package id="Microsoft.VCRTForwarders.140" version="1.0.4" targetFramework="native" />

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.1.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210309.3" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.210825.3" targetFramework="native" />
</packages>

View file

@ -3,13 +3,13 @@
<Import Project="$(MSBuildThisFileDirectory)common.build.post.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View file

@ -8,7 +8,7 @@
<Import Project="..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210309.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<Import Project="$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(OpenConsoleDir)\packages\Microsoft.Windows.CppWinRT.2.0.210825.3\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<CppWinRTHeapEnforcement>AnyValueHereWillDisableTheOptOut</CppWinRTHeapEnforcement>

View file

@ -79,4 +79,13 @@
<!-- This feature will not ship to Stable until it is complete. -->
<alwaysDisabledReleaseTokens />
</feature>
<feature>
<name>Feature_PersistedWindowLayout</name>
<description>Whether to allow the user to enable persisted window layout saving and loading</description>
<id>766</id>
<stage>AlwaysEnabled</stage>
<!-- This feature will not ship to Stable until it is complete. -->
<alwaysDisabledReleaseTokens />
</feature>
</featureStaging>

View file

@ -141,6 +141,8 @@ public:
const DispatchTypes::DrcsFontUsage fontUsage,
const VTParameter cellHeight,
const DispatchTypes::DrcsCharsetSize charsetSize) = 0; // DECDLD
virtual StringHandler RequestSetting() = 0; // DECRQSS
};
inline Microsoft::Console::VirtualTerminal::ITermDispatch::~ITermDispatch() {}
#pragma warning(pop)

View file

@ -2527,6 +2527,152 @@ ITermDispatch::StringHandler AdaptDispatch::DownloadDRCS(const size_t fontNumber
};
}
// Method Description:
// - DECRQSS - Requests the state of a VT setting. The value being queried is
// identified by the intermediate and final characters of its control
// sequence, which are passed to the string handler.
// Arguments:
// - None
// Return Value:
// - a function to receive the VTID of the setting being queried
ITermDispatch::StringHandler AdaptDispatch::RequestSetting()
{
// We use a VTIDBuilder to parse the characters in the control string into
// an ID which represents the setting being queried. If the given ID isn't
// supported, we respond with an error sequence: DCS 0 $ r ST. Note that
// this is the opposite of what is documented in most DEC manuals, which
// say that 0 is for a valid response, and 1 is for an error. The correct
// interpretation is documented in the DEC STD 070 reference.
const auto idBuilder = std::make_shared<VTIDBuilder>();
return [=](const auto ch) {
if (ch >= '\x40' && ch <= '\x7e')
{
const auto id = idBuilder->Finalize(ch);
switch (id)
{
case VTID('m'):
_ReportSGRSetting();
break;
case VTID('r'):
_ReportDECSTBMSetting();
break;
default:
_WriteResponse(L"\033P0$r\033\\");
break;
}
return false;
}
else
{
if (ch >= '\x20' && ch <= '\x2f')
{
idBuilder->AddIntermediate(ch);
}
return true;
}
};
}
// Method Description:
// - Reports the current SGR attributes in response to a DECRQSS query.
// Arguments:
// - None
// Return Value:
// - None
void AdaptDispatch::_ReportSGRSetting() const
{
// A valid response always starts with DCS 1 $ r.
// Then the '0' parameter is to reset the SGR attributes to the defaults.
std::wstring response = L"\033P1$r0";
TextAttribute attr;
if (_pConApi->PrivateGetTextAttributes(attr))
{
// For each boolean attribute that is set, we add the appropriate
// parameter value to the response string.
const auto addAttribute = [&](const auto parameter, const auto enabled) {
if (enabled)
{
response += parameter;
}
};
addAttribute(L";1", attr.IsBold());
addAttribute(L";2", attr.IsFaint());
addAttribute(L";3", attr.IsItalic());
addAttribute(L";4", attr.IsUnderlined());
addAttribute(L";5", attr.IsBlinking());
addAttribute(L";7", attr.IsReverseVideo());
addAttribute(L";8", attr.IsInvisible());
addAttribute(L";9", attr.IsCrossedOut());
addAttribute(L";21", attr.IsDoublyUnderlined());
addAttribute(L";53", attr.IsOverlined());
// We also need to add the appropriate color encoding parameters for
// both the foreground and background colors.
const auto addColor = [&](const auto base, const auto color) {
const auto iterator = std::back_insert_iterator(response);
if (color.IsIndex16())
{
const auto index = XtermToWindowsIndex(color.GetIndex());
const auto colorParameter = base + (index >= 8 ? 60 : 0) + (index % 8);
fmt::format_to(iterator, FMT_STRING(L";{}"), colorParameter);
}
else if (color.IsIndex256())
{
const auto index = Xterm256ToWindowsIndex(color.GetIndex());
fmt::format_to(iterator, FMT_STRING(L";{};5;{}"), base + 8, index);
}
else if (color.IsRgb())
{
const auto r = GetRValue(color.GetRGB());
const auto g = GetGValue(color.GetRGB());
const auto b = GetBValue(color.GetRGB());
fmt::format_to(iterator, FMT_STRING(L";{};2;{};{};{}"), base + 8, r, g, b);
}
};
addColor(30, attr.GetForeground());
addColor(40, attr.GetBackground());
}
// The 'm' indicates this is an SGR response, and ST ends the sequence.
response += L"m\033\\";
_WriteResponse(response);
}
// Method Description:
// - Reports the DECSTBM margin range in response to a DECRQSS query.
// Arguments:
// - None
// Return Value:
// - None
void AdaptDispatch::_ReportDECSTBMSetting() const
{
// A valid response always starts with DCS 1 $ r.
std::wstring response = L"\033P1$r";
CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 };
csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
if (_pConApi->GetConsoleScreenBufferInfoEx(csbiex))
{
auto marginTop = _scrollMargins.Top + 1;
auto marginBottom = _scrollMargins.Bottom + 1;
// If the margin top is greater than or equal to the bottom, then the
// margins aren't actually set, so we need to return the full height
// of the window for the margin range.
if (marginTop >= marginBottom)
{
marginTop = 1;
marginBottom = csbiex.srWindow.Bottom - csbiex.srWindow.Top;
}
const auto iterator = std::back_insert_iterator(response);
fmt::format_to(iterator, FMT_STRING(L"{};{}"), marginTop, marginBottom);
}
// The 'r' indicates this is an DECSTBM response, and ST ends the sequence.
response += L"r\033\\";
_WriteResponse(response);
}
// Routine Description:
// - Determines whether we should pass any sequence that manipulates
// TerminalInput's input generator through the PTY. It encapsulates

View file

@ -140,6 +140,8 @@ namespace Microsoft::Console::VirtualTerminal
const VTParameter cellHeight,
const DispatchTypes::DrcsCharsetSize charsetSize) override; // DECDLD
StringHandler RequestSetting() override; // DECRQSS
private:
enum class ScrollDirection
{
@ -189,6 +191,9 @@ namespace Microsoft::Console::VirtualTerminal
void _ResetTabStops() noexcept;
void _InitTabStopsForWidth(const size_t width);
void _ReportSGRSetting() const;
void _ReportDECSTBMSetting() const;
bool _ShouldPassThroughInputModeChange() const;
std::vector<bool> _tabStopColumns;

View file

@ -132,4 +132,6 @@ public:
const DispatchTypes::DrcsFontUsage /*fontUsage*/,
const VTParameter /*cellHeight*/,
const DispatchTypes::DrcsCharsetSize /*charsetSize*/) noexcept override { return nullptr; }
StringHandler RequestSetting() noexcept override { return nullptr; }; // DECRQSS
};

View file

@ -1990,6 +1990,109 @@ public:
VERIFY_IS_FALSE(_pDispatch.get()->RequestTerminalParameters(DispatchTypes::ReportingPermission::Unsolicited));
}
TEST_METHOD(RequestSettingsTests)
{
const auto requestSetting = [=](const std::wstring_view settingId = {}) {
const auto stringHandler = _pDispatch.get()->RequestSetting();
for (auto ch : settingId)
{
stringHandler(ch);
}
stringHandler(L'\033'); // String terminator
};
Log::Comment(L"Requesting DECSTBM margins (5 to 10).");
_testGetSet->PrepData();
_pDispatch.get()->SetTopBottomScrollingMargins(5, 10);
requestSetting(L"r");
_testGetSet->ValidateInputEvent(L"\033P1$r5;10r\033\\");
Log::Comment(L"Requesting DECSTBM margins (full screen).");
_testGetSet->PrepData();
// Set screen height to 25 - this will be the expected margin range.
_testGetSet->_viewport.Bottom = _testGetSet->_viewport.Top + 25;
_pDispatch.get()->SetTopBottomScrollingMargins(0, 0);
requestSetting(L"r");
_testGetSet->ValidateInputEvent(L"\033P1$r1;25r\033\\");
Log::Comment(L"Requesting SGR attributes (default).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0m\033\\");
Log::Comment(L"Requesting SGR attributes (bold, underlined, reversed).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetBold(true);
_testGetSet->_attribute.SetUnderlined(true);
_testGetSet->_attribute.SetReverseVideo(true);
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;1;4;7m\033\\");
Log::Comment(L"Requesting SGR attributes (faint, blinking, invisible).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetFaint(true);
_testGetSet->_attribute.SetBlinking(true);
_testGetSet->_attribute.SetInvisible(true);
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;2;5;8m\033\\");
Log::Comment(L"Requesting SGR attributes (italic, crossed-out).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetItalic(true);
_testGetSet->_attribute.SetCrossedOut(true);
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;3;9m\033\\");
Log::Comment(L"Requesting SGR attributes (doubly underlined, overlined).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetDoublyUnderlined(true);
_testGetSet->_attribute.SetOverlined(true);
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;21;53m\033\\");
Log::Comment(L"Requesting SGR attributes (standard colors).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetIndexedForeground((BYTE)::XtermToWindowsIndex(3));
_testGetSet->_attribute.SetIndexedBackground((BYTE)::XtermToWindowsIndex(6));
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;33;46m\033\\");
Log::Comment(L"Requesting SGR attributes (AIX colors).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetIndexedForeground((BYTE)::XtermToWindowsIndex(14));
_testGetSet->_attribute.SetIndexedBackground((BYTE)::XtermToWindowsIndex(11));
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;96;103m\033\\");
Log::Comment(L"Requesting SGR attributes (ITU indexed colors).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetIndexedForeground256(123);
_testGetSet->_attribute.SetIndexedBackground256(45);
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;38;5;123;48;5;45m\033\\");
Log::Comment(L"Requesting SGR attributes (ITU RGB colors).");
_testGetSet->PrepData();
_testGetSet->_attribute = {};
_testGetSet->_attribute.SetForeground(RGB(12, 34, 56));
_testGetSet->_attribute.SetBackground(RGB(65, 43, 21));
requestSetting(L"m");
_testGetSet->ValidateInputEvent(L"\033P1$r0;38;2;12;34;56;48;2;65;43;21m\033\\");
Log::Comment(L"Requesting an unsupported setting.");
_testGetSet->PrepData();
requestSetting(L"x");
_testGetSet->ValidateInputEvent(L"\033P0$r\033\\");
}
TEST_METHOD(CursorKeysModeTest)
{
Log::Comment(L"Starting test...");

View file

@ -661,6 +661,9 @@ IStateMachineEngine::StringHandler OutputStateMachineEngine::ActionDcsDispatch(c
parameters.at(6),
parameters.at(7));
break;
case DcsActionCodes::DECRQSS_RequestSetting:
handler = _dispatch->RequestSetting();
break;
default:
handler = nullptr;
break;

View file

@ -147,6 +147,7 @@ namespace Microsoft::Console::VirtualTerminal
enum DcsActionCodes : uint64_t
{
DECDLD_DownloadDRCS = VTID("{"),
DECRQSS_RequestSetting = VTID("$q")
};
enum Vt52ActionCodes : uint64_t