Merge remote-tracking branch 'origin/main' into feature/llm

This commit is contained in:
Console Service Bot 2025-11-21 02:31:46 +00:00
commit 7f33cc8d53
28 changed files with 314 additions and 124 deletions

View File

@ -1139,6 +1139,7 @@ NOYIELD
NOZORDER
NPFS
nrcs
NRNW
NSTATUS
ntapi
ntdef

View File

@ -2628,11 +2628,8 @@ void TextBuffer::_AppendRTFText(std::string& contentBuilder, const std::wstring_
}
}
void TextBuffer::SerializeToPath(const wchar_t* destination) const
void TextBuffer::SerializeTo(HANDLE handle) const
{
const wil::unique_handle file{ CreateFileW(destination, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) };
THROW_LAST_ERROR_IF(!file);
static constexpr size_t writeThreshold = 32 * 1024;
std::wstring buffer;
buffer.reserve(writeThreshold + writeThreshold / 2);
@ -2661,7 +2658,7 @@ void TextBuffer::SerializeToPath(const wchar_t* destination) const
{
const auto fileSize = gsl::narrow<DWORD>(buffer.size() * sizeof(wchar_t));
DWORD bytesWritten = 0;
THROW_IF_WIN32_BOOL_FALSE(WriteFile(file.get(), buffer.data(), fileSize, &bytesWritten, nullptr));
THROW_IF_WIN32_BOOL_FALSE(WriteFile(handle, buffer.data(), fileSize, &bytesWritten, nullptr));
THROW_WIN32_IF_MSG(ERROR_WRITE_FAULT, bytesWritten != fileSize, "failed to write");
buffer.clear();
}

View File

@ -288,7 +288,7 @@ public:
const bool isIntenseBold,
std::function<std::tuple<COLORREF, COLORREF, COLORREF>(const TextAttribute&)> GetAttributeColors) const noexcept;
void SerializeToPath(const wchar_t* destination) const;
void SerializeTo(HANDLE handle) const;
struct PositionInformation
{

View File

@ -8,8 +8,7 @@ namespace TerminalApp
None,
Content,
MovePane,
PersistLayout,
PersistAll
Persist,
};
runtimeclass BellEventArgs

View File

@ -2420,7 +2420,7 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::PersistState(bool serializeBuffer)
void TerminalPage::PersistState()
{
// This method may be called for a window even if it hasn't had a tab yet or lost all of them.
// We shouldn't persist such windows.
@ -2435,7 +2435,7 @@ namespace winrt::TerminalApp::implementation
for (auto tab : _tabs)
{
auto t = winrt::get_self<implementation::Tab>(tab);
auto tabActions = t->BuildStartupActions(serializeBuffer ? BuildStartupKind::PersistAll : BuildStartupKind::PersistLayout);
auto tabActions = t->BuildStartupActions(BuildStartupKind::Persist);
actions.insert(actions.end(), std::make_move_iterator(tabActions.begin()), std::make_move_iterator(tabActions.end()));
}
@ -2521,6 +2521,29 @@ namespace winrt::TerminalApp::implementation
CloseWindowRequested.raise(*this, nullptr);
}
std::vector<IPaneContent> TerminalPage::Panes() const
{
std::vector<IPaneContent> panes;
for (const auto tab : _tabs)
{
const auto impl = _GetTabImpl(tab);
if (!impl)
{
continue;
}
impl->GetRootPane()->WalkTree([&](auto&& pane) {
if (auto content = pane->GetContent())
{
panes.push_back(std::move(content));
}
});
}
return panes;
}
// Method Description:
// - Move the viewport of the terminal of the currently focused tab up or
// down a number of lines.
@ -3729,9 +3752,12 @@ namespace winrt::TerminalApp::implementation
if (hasSessionId)
{
using namespace std::string_view_literals;
const auto settingsDir = CascadiaSettings::SettingsDirectory();
const auto idStr = Utils::GuidToPlainString(sessionId);
const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr);
const auto admin = IsRunningElevated();
const auto filenamePrefix = admin ? L"elevated_"sv : L"buffer_"sv;
const auto path = fmt::format(FMT_COMPILE(L"{}\\{}{}.txt"), settingsDir, filenamePrefix, sessionId);
control.RestoreFromPath(path);
}

View File

@ -122,7 +122,8 @@ namespace winrt::TerminalApp::implementation
safe_void_coroutine RequestQuit();
safe_void_coroutine CloseWindow();
void PersistState(bool serializeBuffer);
void PersistState();
std::vector<IPaneContent> Panes() const;
void ToggleFocusMode();
void ToggleFullscreen();

View File

@ -140,22 +140,16 @@ namespace winrt::TerminalApp::implementation
// "attach existing" rather than a "create"
args.ContentId(_control.ContentId());
break;
case BuildStartupKind::PersistAll:
case BuildStartupKind::Persist:
{
const auto connection = _control.Connection();
const auto id = connection ? connection.SessionId() : winrt::guid{};
if (id != winrt::guid{})
{
const auto settingsDir = CascadiaSettings::SettingsDirectory();
const auto idStr = ::Microsoft::Console::Utils::GuidToPlainString(id);
const auto path = fmt::format(FMT_COMPILE(L"{}\\buffer_{}.txt"), settingsDir, idStr);
_control.PersistToPath(path);
args.SessionId(id);
}
break;
}
case BuildStartupKind::PersistLayout:
default:
break;
}

View File

@ -253,11 +253,11 @@ namespace winrt::TerminalApp::implementation
AppLogic::Current()->NotifyRootInitialized();
}
void TerminalWindow::PersistState(bool serializeBuffer)
void TerminalWindow::PersistState()
{
if (_root)
{
_root->PersistState(serializeBuffer);
_root->PersistState();
}
}
@ -830,6 +830,11 @@ namespace winrt::TerminalApp::implementation
return _root.as<winrt::Windows::UI::Xaml::Controls::Control>();
}
winrt::Windows::Foundation::Collections::IVector<IPaneContent> TerminalWindow::Panes() const
{
return winrt::single_threaded_vector(_root->Panes());
}
// Method Description:
// - Gets the title of the currently focused terminal control. If there
// isn't a control selected for any reason, returns "Terminal"

View File

@ -71,7 +71,7 @@ namespace winrt::TerminalApp::implementation
void Create();
void PersistState(bool serializeBuffer);
void PersistState();
void UpdateSettings(winrt::TerminalApp::SettingsLoadEventArgs args);
@ -111,6 +111,7 @@ namespace winrt::TerminalApp::implementation
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
Windows::UI::Xaml::UIElement GetRoot() noexcept;
winrt::Windows::Foundation::Collections::IVector<IPaneContent> Panes() const;
hstring Title();
void TitlebarClicked();

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "IPaneContent.idl";
import "TerminalPage.idl";
import "ShortcutActionDispatch.idl";
@ -59,9 +60,10 @@ namespace TerminalApp
Boolean ShouldImmediatelyHandoffToElevated();
void HandoffToElevated();
void PersistState(Boolean serializeBuffer);
void PersistState();
Windows.UI.Xaml.UIElement GetRoot();
IVector<IPaneContent> Panes { get; };
String Title { get; };
Boolean FocusMode { get; };

View File

@ -1815,15 +1815,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_closeConnection();
}
void ControlCore::PersistToPath(const wchar_t* path) const
void ControlCore::PersistTo(HANDLE handle) const
{
const auto lock = _terminal->LockForReading();
_terminal->SerializeMainBuffer(path);
_terminal->SerializeMainBuffer(handle);
}
void ControlCore::RestoreFromPath(const wchar_t* path) const
{
const wil::unique_handle file{ CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
wil::unique_handle file{ CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr) };
// This block of code exists temporarily to fix buffer dumps that were
// previously persisted as "buffer_" but really should be named "elevated_".
// If loading the properly named file fails, retry with the old name.
if (!file)
{
static constexpr std::wstring_view needle{ L"\\elevated_" };
// Check if the path contains "\elevated_", indicating that we're in an elevated session.
const std::wstring_view pathView{ path };
const auto idx = pathView.find(needle);
if (idx != std::wstring_view::npos)
{
// If so, try to open the file with "\buffer_" instead, which is what we previously used.
std::wstring altPath{ pathView };
altPath.replace(idx, needle.size(), L"\\buffer_");
file.reset(CreateFileW(altPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr));
// If the alternate file is found, move it to the correct location.
if (file)
{
LOG_IF_WIN32_BOOL_FALSE(MoveFileW(altPath.c_str(), path));
}
}
}
if (!file)
{
return;

View File

@ -153,7 +153,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode);
void Close();
void PersistToPath(const wchar_t* path) const;
void PersistTo(HANDLE handle) const;
void RestoreFromPath(const wchar_t* path) const;
void ClearQuickFix();

View File

@ -2578,7 +2578,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_restorePath = std::move(path);
}
void TermControl::PersistToPath(const winrt::hstring& path) const
void TermControl::PersistTo(int64_t handle) const
{
// Don't persist us if we weren't ever initialized. In that case, we
// never got an initial size, never instantiated a buffer, and didn't
@ -2590,7 +2590,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// file then.
if (_initializedTerminal)
{
winrt::get_self<ControlCore>(_core)->PersistToPath(path.c_str());
winrt::get_self<ControlCore>(_core)->PersistTo(reinterpret_cast<HANDLE>(handle));
}
}

View File

@ -69,7 +69,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool SwitchSelectionEndpoint();
bool ExpandSelectionToWord();
void RestoreFromPath(winrt::hstring path);
void PersistToPath(const winrt::hstring& path) const;
void PersistTo(int64_t handle) const;
void OpenCWD();
void Close();
Windows::Foundation::Size CharacterDimensions() const;

View File

@ -106,7 +106,7 @@ namespace Microsoft.Terminal.Control
Boolean ExpandSelectionToWord();
void ClearBuffer(ClearBufferType clearType);
void RestoreFromPath(String path);
void PersistToPath(String path);
void PersistTo(Int64 handle);
void OpenCWD();
void Close();
Windows.Foundation.Size CharacterDimensions { get; };

View File

@ -1523,9 +1523,9 @@ std::wstring Terminal::CurrentCommand() const
return _activeBuffer().CurrentCommand();
}
void Terminal::SerializeMainBuffer(const wchar_t* destination) const
void Terminal::SerializeMainBuffer(HANDLE handle) const
{
_mainBuffer->SerializeToPath(destination);
_mainBuffer->SerializeTo(handle);
}
void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Terminal::Core::MatchMode matchMode)

View File

@ -127,7 +127,7 @@ public:
std::wstring CurrentCommand() const;
void SerializeMainBuffer(const wchar_t* destination) const;
void SerializeMainBuffer(HANDLE handle) const;
#pragma region ITerminalApi
// These methods are defined in TerminalApi.cpp

View File

@ -233,7 +233,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IObservableVector<winrt::hstring> _FontFeaturesNames;
std::wstring _fontNameFilter;
bool _ShowAllFonts = false;
bool _suppressFontFaceBoxList = false;
static void _ViewModelChanged(const Windows::UI::Xaml::DependencyObject& d, const Windows::UI::Xaml::DependencyPropertyChangedEventArgs& e);

View File

@ -29,7 +29,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_FontList{ nullptr };
Windows::Foundation::Collections::IVector<IInspectable> ProfileViewModel::_BuiltInIcons{ nullptr };
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> ProfileViewModel::_BuiltInIcons{ nullptr };
static constexpr std::wstring_view HideIconValue{ L"none" };
@ -114,7 +114,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (viewModelProperty == L"CurrentBuiltInIcon")
{
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.as<Editor::EnumEntry>().EnumValue()));
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
}
else if (viewModelProperty == L"CurrentEmojiIcon")
{
@ -193,12 +193,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void ProfileViewModel::_UpdateBuiltInIcons()
{
std::vector<IInspectable> builtInIcons;
std::vector<Editor::EnumEntry> builtInIcons;
for (auto& [val, name] : s_SegoeFluentIcons)
{
builtInIcons.emplace_back(make<EnumEntry>(hstring{ name }, box_value(val)));
}
_BuiltInIcons = single_threaded_vector<IInspectable>(std::move(builtInIcons));
_BuiltInIcons = single_threaded_observable_vector<Editor::EnumEntry>(std::move(builtInIcons));
}
void ProfileViewModel::_DeduceCurrentIconType()
@ -236,7 +236,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
for (uint32_t i = 0; i < _BuiltInIcons.Size(); i++)
{
const auto& builtIn = _BuiltInIcons.GetAt(i);
if (profileIcon == unbox_value<hstring>(builtIn.as<Editor::EnumEntry>().EnumValue()))
if (profileIcon == unbox_value<hstring>(builtIn.EnumValue()))
{
_CurrentBuiltInIcon = builtIn;
return;
@ -695,7 +695,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (_CurrentBuiltInIcon)
{
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.as<Editor::EnumEntry>().EnumValue()));
IconPath(unbox_value<hstring>(_CurrentBuiltInIcon.EnumValue()));
}
break;
}

View File

@ -49,7 +49,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static void UpdateFontList() noexcept;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> CompleteFontList() noexcept { return _FontList; };
static Windows::Foundation::Collections::IObservableVector<Editor::Font> MonospaceFontList() noexcept { return _MonospaceFontList; };
static Windows::Foundation::Collections::IVector<IInspectable> BuiltInIcons() noexcept { return _BuiltInIcons; };
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> BuiltInIcons() noexcept { return _BuiltInIcons; };
ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings, const Windows::UI::Core::CoreDispatcher& dispatcher);
Control::IControlSettings TermSettings() const;
@ -134,7 +134,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::BellSoundViewModel>, CurrentBellSounds);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::IInspectable, CurrentBuiltInIcon);
VIEW_MODEL_OBSERVABLE_PROPERTY(Editor::EnumEntry, CurrentBuiltInIcon, nullptr);
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon);
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid);
@ -197,7 +197,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void _MarkDuplicateBellSoundDirectories();
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList;
static Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> _BuiltInIcons;
static Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _BuiltInIcons;
Model::CascadiaSettings _appSettings;
Editor::AppearanceViewModel _unfocusedAppearanceViewModel;

View File

@ -116,8 +116,8 @@ namespace Microsoft.Terminal.Settings.Editor
Boolean UsingImageIcon { get; };
String IconPath;
IInspectable CurrentBuiltInIcon;
Windows.Foundation.Collections.IVector<IInspectable> BuiltInIcons { get; };
EnumEntry CurrentBuiltInIcon;
Windows.Foundation.Collections.IObservableVector<EnumEntry> BuiltInIcons { get; };
String TabTitlePreview { get; };
String AnswerbackMessagePreview { get; };

View File

@ -171,8 +171,76 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
}
Windows::UI::Xaml::Controls::IconSource Profiles_Base::BuiltInIconConverter(const IInspectable& iconVal)
IconSource Profiles_Base::BuiltInIconConverter(const IInspectable& iconVal)
{
return Microsoft::Terminal::UI::IconPathConverter::IconSourceWUX(unbox_value<hstring>(iconVal));
}
void Profiles_Base::BuiltInIconPicker_GotFocus(const IInspectable& sender, const RoutedEventArgs& /*e*/)
{
_updateIconFilter({});
sender.as<AutoSuggestBox>().IsSuggestionListOpen(true);
}
void Profiles_Base::BuiltInIconPicker_QuerySubmitted(const AutoSuggestBox& /*sender*/, const AutoSuggestBoxQuerySubmittedEventArgs& e)
{
const auto iconEntry = unbox_value_or<EnumEntry>(e.ChosenSuggestion(), nullptr);
if (!iconEntry)
{
return;
}
_Profile.CurrentBuiltInIcon(iconEntry);
}
void Profiles_Base::BuiltInIconPicker_TextChanged(const AutoSuggestBox& sender, const AutoSuggestBoxTextChangedEventArgs& e)
{
if (e.Reason() != AutoSuggestionBoxTextChangeReason::UserInput)
{
return;
}
std::wstring_view filter{ sender.Text() };
filter = til::trim(filter, L' ');
_updateIconFilter(filter);
}
void Profiles_Base::_updateIconFilter(std::wstring_view filter)
{
if (_iconFilter != filter)
{
_filteredBuiltInIcons = nullptr;
_iconFilter = filter;
_updateFilteredIconList();
PropertyChanged.raise(*this, PropertyChangedEventArgs{ L"FilteredBuiltInIconList" });
}
}
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> Profiles_Base::FilteredBuiltInIconList()
{
if (!_filteredBuiltInIcons)
{
_updateFilteredIconList();
}
return _filteredBuiltInIcons;
}
void Profiles_Base::_updateFilteredIconList()
{
_filteredBuiltInIcons = ProfileViewModel::BuiltInIcons();
if (_iconFilter.empty())
{
return;
}
// Find matching icons and populate the filtered list
std::vector<Editor::EnumEntry> filtered;
filtered.reserve(_filteredBuiltInIcons.Size());
for (const auto& icon : _filteredBuiltInIcons)
{
if (til::contains_linguistic_insensitive(icon.EnumName(), _iconFilter))
{
filtered.emplace_back(icon);
}
}
_filteredBuiltInIcons = winrt::single_threaded_observable_vector(std::move(filtered));
}
}

View File

@ -25,6 +25,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Advanced_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void DeleteConfirmation_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> FilteredBuiltInIconList();
void BuiltInIconPicker_GotFocus(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void BuiltInIconPicker_TextChanged(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs& e);
void BuiltInIconPicker_QuerySubmitted(const winrt::Windows::UI::Xaml::Controls::AutoSuggestBox& sender, const Windows::UI::Xaml::Controls::AutoSuggestBoxQuerySubmittedEventArgs& e);
static Windows::UI::Xaml::Controls::IconSource BuiltInIconConverter(const Windows::Foundation::IInspectable& iconVal);
til::property_changed_event PropertyChanged;
@ -34,6 +39,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
Editor::IHostedInWindow _windowRoot;
Windows::Foundation::Collections::IObservableVector<Editor::EnumEntry> _filteredBuiltInIcons;
std::wstring _iconFilter;
void _updateIconFilter(std::wstring_view filter);
void _updateFilteredIconList();
};
};

View File

@ -9,6 +9,7 @@ namespace Microsoft.Terminal.Settings.Editor
{
Profiles_Base();
ProfileViewModel Profile { get; };
Windows.Foundation.Collections.IObservableVector<EnumEntry> FilteredBuiltInIconList { get; };
static Windows.UI.Xaml.Controls.IconSource BuiltInIconConverter(IInspectable iconVal);
}

View File

@ -142,12 +142,16 @@
</ComboBox>
<!-- Built-In Icon -->
<ComboBox x:Uid="Profile_BuiltInIcon"
Grid.Column="1"
ItemsSource="{x:Bind Profile.BuiltInIcons}"
SelectedItem="{x:Bind Profile.CurrentBuiltInIcon, Mode=TwoWay}"
Visibility="{x:Bind Profile.UsingBuiltInIcon, Mode=OneWay}">
<ComboBox.ItemTemplate>
<AutoSuggestBox x:Uid="Profile_BuiltInIcon"
Grid.Column="1"
GotFocus="BuiltInIconPicker_GotFocus"
ItemsSource="{x:Bind FilteredBuiltInIconList, Mode=OneWay}"
QuerySubmitted="BuiltInIconPicker_QuerySubmitted"
Text="{x:Bind Profile.CurrentBuiltInIcon.EnumName, Mode=OneWay}"
TextBoxStyle="{StaticResource TextBoxSettingStyle}"
TextChanged="BuiltInIconPicker_TextChanged"
Visibility="{x:Bind Profile.UsingBuiltInIcon, Mode=OneWay}">
<AutoSuggestBox.ItemTemplate>
<DataTemplate x:DataType="local:EnumEntry">
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
@ -163,8 +167,8 @@
Text="{x:Bind EnumName}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>
<!-- Image (File) Icon -->
<TextBox x:Uid="Profile_IconBox"

View File

@ -10,6 +10,8 @@
#include <WtExeUtils.h>
#include <til/hash.h>
#include <wil/token_helpers.h>
#include <winrt/TerminalApp.h>
#include <sddl.h>
#include "AppHost.h"
#include "resource.h"
@ -1038,12 +1040,12 @@ void WindowEmperor::_setupSessionPersistence(bool enabled)
}
_persistStateTimer.Interval(std::chrono::minutes(5));
_persistStateTimer.Tick([&](auto&&, auto&&) {
_persistState(ApplicationState::SharedInstance(), false);
_persistState(ApplicationState::SharedInstance());
});
_persistStateTimer.Start();
}
void WindowEmperor::_persistState(const ApplicationState& state, bool serializeBuffer) const
void WindowEmperor::_persistState(const ApplicationState& state) const
{
// Calling an `ApplicationState` setter triggers a write to state.json.
// With this if condition we avoid an unnecessary write when persistence is disabled.
@ -1052,11 +1054,11 @@ void WindowEmperor::_persistState(const ApplicationState& state, bool serializeB
state.PersistedWindowLayouts(nullptr);
}
if (_app.Logic().Settings().GlobalSettings().ShouldUsePersistedLayout())
if (_app.Logic().Settings().GlobalSettings().FirstWindowPreference() != FirstWindowPreference::DefaultProfile)
{
for (const auto& w : _windows)
{
w->Logic().PersistState(serializeBuffer);
w->Logic().PersistState();
}
}
@ -1066,6 +1068,8 @@ void WindowEmperor::_persistState(const ApplicationState& state, bool serializeB
void WindowEmperor::_finalizeSessionPersistence() const
{
using namespace std::string_view_literals;
if (_skipPersistence)
{
// We received WM_ENDSESSION and persisted the state.
@ -1075,78 +1079,106 @@ void WindowEmperor::_finalizeSessionPersistence() const
const auto state = ApplicationState::SharedInstance();
_persistState(state, _app.Logic().Settings().GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedLayoutAndContent);
_persistState(state);
const auto firstWindowPreference = _app.Logic().Settings().GlobalSettings().FirstWindowPreference();
const auto wantsPersistence = firstWindowPreference != FirstWindowPreference::DefaultProfile;
// If we neither need a cleanup nor want to persist, we can exit early.
if (!_needsPersistenceCleanup && !wantsPersistence)
{
return;
}
const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } };
const auto admin = _app.Logic().IsRunningElevated();
const auto filenamePrefix = admin ? L"elevated_"sv : L"buffer_"sv;
const auto persistBuffers = firstWindowPreference == FirstWindowPreference::PersistedLayoutAndContent;
std::unordered_set<std::wstring, til::transparent_hstring_hash, til::transparent_hstring_equal_to> bufferFilenames;
if (persistBuffers)
{
// If the app is running elevated, we create files with a mandatory
// integrity label (ML) of "High" (HI) for reading and writing (NR, NW).
wil::unique_hlocal_security_descriptor sd;
SECURITY_ATTRIBUTES sa{};
if (admin)
{
unsigned long cb;
THROW_IF_WIN32_BOOL_FALSE(ConvertStringSecurityDescriptorToSecurityDescriptorW(L"S:(ML;;NRNW;;;HI)", SDDL_REVISION_1, wil::out_param_ptr<PSECURITY_DESCRIPTOR*>(sd), &cb));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = sd.get();
}
// Persist all terminal buffers to "buffer_{guid}.txt" files.
// We remember the filenames so that we can clean up old ones later.
for (const auto& w : _windows)
{
const auto panes = w->Logic().Panes();
for (const auto pane : panes)
{
try
{
const auto term = pane.try_as<winrt::TerminalApp::ITerminalPaneContent>();
if (!term)
{
continue;
}
const auto control = term.GetTermControl();
if (!control)
{
continue;
}
const auto connection = control.Connection();
if (!connection)
{
continue;
}
const auto sessionId = connection.SessionId();
if (sessionId == winrt::guid{})
{
continue;
}
auto filename = fmt::format(FMT_COMPILE(L"{}{}.txt"), filenamePrefix, sessionId);
const auto path = settingsDirectory / filename;
if (wil::unique_hfile file{ CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr) })
{
control.PersistTo(reinterpret_cast<int64_t>(file.get()));
bufferFilenames.emplace(std::move(filename));
}
}
CATCH_LOG();
}
}
}
// Now remove the "buffer_{guid}.txt" files that shouldn't be there.
if (_needsPersistenceCleanup)
{
// Get the "buffer_{guid}.txt" files that we expect to be there
std::unordered_set<winrt::guid> sessionIds;
if (const auto layouts = state.PersistedWindowLayouts())
const auto filter = fmt::format(FMT_COMPILE(L"{}\\{}*"), settingsDirectory.native(), filenamePrefix);
// This could also use std::filesystem::directory_iterator.
// I was just slightly bothered by how it doesn't have a O(1) .filename()
// function, even though the underlying Win32 APIs provide it for free.
// Both work fine.
WIN32_FIND_DATAW ffd;
const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) };
if (!handle)
{
for (const auto& windowLayout : layouts)
{
for (const auto& actionAndArgs : windowLayout.TabLayout())
{
const auto args = actionAndArgs.Args();
NewTerminalArgs terminalArgs{ nullptr };
if (const auto tabArgs = args.try_as<NewTabArgs>())
{
terminalArgs = tabArgs.ContentArgs().try_as<NewTerminalArgs>();
}
else if (const auto paneArgs = args.try_as<SplitPaneArgs>())
{
terminalArgs = paneArgs.ContentArgs().try_as<NewTerminalArgs>();
}
if (terminalArgs)
{
sessionIds.emplace(terminalArgs.SessionId());
}
}
}
return;
}
// Remove the "buffer_{guid}.txt" files that shouldn't be there
// e.g. "buffer_FD40D746-163E-444C-B9B2-6A3EA2B26722.txt"
do
{
const std::filesystem::path settingsDirectory{ std::wstring_view{ CascadiaSettings::SettingsDirectory() } };
const auto filter = settingsDirectory / L"buffer_*";
WIN32_FIND_DATAW ffd;
// This could also use std::filesystem::directory_iterator.
// I was just slightly bothered by how it doesn't have a O(1) .filename()
// function, even though the underlying Win32 APIs provide it for free.
// Both work fine.
const wil::unique_hfind handle{ FindFirstFileExW(filter.c_str(), FindExInfoBasic, &ffd, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH) };
if (!handle)
const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName));
const std::wstring_view filename{ &ffd.cFileName[0], nameLen };
if (!bufferFilenames.contains(filename))
{
return;
std::filesystem::remove(settingsDirectory / filename);
}
do
{
const auto nameLen = wcsnlen_s(&ffd.cFileName[0], ARRAYSIZE(ffd.cFileName));
const std::wstring_view name{ &ffd.cFileName[0], nameLen };
if (nameLen != 47)
{
continue;
}
wchar_t guidStr[39];
guidStr[0] = L'{';
memcpy(&guidStr[1], name.data() + 7, 36 * sizeof(wchar_t));
guidStr[37] = L'}';
guidStr[38] = L'\0';
const auto id = Utils::GuidFromString(&guidStr[0]);
if (!sessionIds.contains(id))
{
std::filesystem::remove(settingsDirectory / name);
}
} while (FindNextFileW(handle.get(), &ffd));
}
} while (FindNextFileW(handle.get(), &ffd));
}
}

View File

@ -65,7 +65,7 @@ private:
void _unregisterHotKey(int index) noexcept;
void _setupGlobalHotkeys();
void _setupSessionPersistence(bool enabled);
void _persistState(const winrt::Microsoft::Terminal::Settings::Model::ApplicationState& state, bool serializeBuffer) const;
void _persistState(const winrt::Microsoft::Terminal::Settings::Model::ApplicationState& state) const;
void _finalizeSessionPersistence() const;
void _checkWindowsForNotificationIcon();

View File

@ -74,6 +74,28 @@ struct fmt::formatter<winrt::hstring, wchar_t> : fmt::formatter<fmt::wstring_vie
}
};
template<>
struct fmt::formatter<winrt::guid, wchar_t> : fmt::formatter<fmt::wstring_view, wchar_t>
{
auto format(const winrt::guid& value, auto& ctx) const
{
return fmt::format_to(
ctx.out(),
L"{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
value.Data1,
value.Data2,
value.Data3,
value.Data4[0],
value.Data4[1],
value.Data4[2],
value.Data4[3],
value.Data4[4],
value.Data4[5],
value.Data4[6],
value.Data4[7]);
}
};
// This is a helper macro for both declaring the signature of an event, and
// defining the body. Winrt events need a method for adding a callback to the
// event and removing the callback. This macro will both declare the method