mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 17:53:06 -06:00
Merge remote-tracking branch 'origin/main' into feature/llm
This commit is contained in:
commit
7f33cc8d53
1
.github/actions/spelling/expect/expect.txt
vendored
1
.github/actions/spelling/expect/expect.txt
vendored
@ -1139,6 +1139,7 @@ NOYIELD
|
||||
NOZORDER
|
||||
NPFS
|
||||
nrcs
|
||||
NRNW
|
||||
NSTATUS
|
||||
ntapi
|
||||
ntdef
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -8,8 +8,7 @@ namespace TerminalApp
|
||||
None,
|
||||
Content,
|
||||
MovePane,
|
||||
PersistLayout,
|
||||
PersistAll
|
||||
Persist,
|
||||
};
|
||||
|
||||
runtimeclass BellEventArgs
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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; };
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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; };
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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; };
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user