Merge remote-tracking branch 'origin/main' into dev/migrie/f/non-terminal-content-elevation-warning
This commit is contained in:
commit
b592155d2b
|
@ -1260,6 +1260,11 @@
|
|||
"default": "false",
|
||||
"description": "When set to true, the Terminal's tray icon will always be shown in the system tray.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"useAcrylicInTabRow": {
|
||||
"default": "false",
|
||||
"description": "When set to true, the tab row will have an acrylic background with 50% opacity.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"actions": {
|
||||
"description": "Properties are specific to each custom action.",
|
||||
|
|
|
@ -29,8 +29,8 @@ Below is the schedule for when milestones will be included in release builds of
|
|||
| 2021-03-01 | [1.7] in Windows Terminal Preview<br>[1.6] in Windows Terminal | [Windows Terminal Preview 1.7 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-7-release/) |
|
||||
| 2021-04-14 | [1.8] in Windows Terminal Preview<br>[1.7] in Windows Terminal | [Windows Terminal Preview 1.8 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-8-release/) |
|
||||
| 2021-05-31 | [1.9] in Windows Terminal Preview<br>[1.8] in Windows Terminal | [Windows Terminal Preview 1.9 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-9-release/) |
|
||||
| 2021-07-31 | 1.10 in Windows Terminal Preview<br>[1.9] in Windows Terminal | |
|
||||
| 2021-08-30 | 1.11 in Windows Terminal Preview<br>1.10 in Windows Terminal | |
|
||||
| 2021-07-14 | [1.10] in Windows Terminal Preview<br>[1.9] in Windows Terminal | [Windows Terminal Preview 1.10 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-10-release/) |
|
||||
| 2021-08-31 | [1.11] in Windows Terminal Preview<br>[1.10] in Windows Terminal | [Windows Terminal Preview 1.11 Release](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/) |
|
||||
| 2021-10-31 | 1.12 in Windows Terminal Preview<br>1.11 in Windows Terminal | |
|
||||
| 2021-11-30 | 2.0 RC in Windows Terminal Preview<br>2.0 RC in Windows Terminal | |
|
||||
| 2021-12-31 | [2.0] in Windows Terminal Preview<br>[2.0] in Windows Terminal | |
|
||||
|
@ -89,6 +89,8 @@ Feature Notes:
|
|||
[1.7]: https://github.com/microsoft/terminal/milestone/32
|
||||
[1.8]: https://github.com/microsoft/terminal/milestone/33
|
||||
[1.9]: https://github.com/microsoft/terminal/milestone/34
|
||||
[1.10]: https://github.com/microsoft/terminal/milestone/35
|
||||
[1.11]: https://github.com/microsoft/terminal/milestone/36
|
||||
[2.0]: https://github.com/microsoft/terminal/milestone/22
|
||||
[#1564]: https://github.com/microsoft/terminal/issues/1564
|
||||
[#6720]: https://github.com/microsoft/terminal/pull/6720
|
||||
|
|
|
@ -613,39 +613,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
return *result;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Helper for doing something on each and every peasant, with no regard
|
||||
// for if the peasant is living or dead.
|
||||
// - We'll try calling callback on every peasant.
|
||||
// - If any single peasant is dead, then we'll call errorCallback, and move on.
|
||||
// - We're taking an errorCallback here, because the thing we usually want
|
||||
// to do is TraceLog a message, but TraceLoggingWrite is actually a macro
|
||||
// that _requires_ the second arg to be a string literal. It can't just be
|
||||
// a variable.
|
||||
// Arguments:
|
||||
// - callback: The function to call on each peasant
|
||||
// - errorCallback: The function to call if a peasant is dead.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_forAllPeasantsIgnoringTheDead(std::function<void(const Remoting::IPeasant&, const uint64_t)> callback,
|
||||
std::function<void(const uint64_t)> errorCallback)
|
||||
{
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(p, id);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
// If this fails, we don't _really_ care. Just move on to the
|
||||
// next one. Someone else will clean up the dead peasant.
|
||||
errorCallback(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - This is an event handler for the IdentifyWindowsRequested event. A
|
||||
// Peasant may raise that event if they want _all_ windows to identify
|
||||
|
@ -660,17 +627,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
// Notify all the peasants to display their ID.
|
||||
auto callback = [](auto&& p, auto&& /*id*/) {
|
||||
const auto callback = [&](const auto& /*id*/, const auto& p) {
|
||||
p.DisplayWindowId();
|
||||
};
|
||||
auto onError = [](auto&& id) {
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_identifyWindows_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
_forAllPeasantsIgnoringTheDead(callback, onError);
|
||||
|
||||
_forEachPeasant(callback, onError);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -812,48 +780,68 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - A map of peasant IDs to their names.
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> Monarch::GetPeasantNames()
|
||||
Windows::Foundation::Collections::IVectorView<PeasantInfo> Monarch::GetPeasantInfos()
|
||||
{
|
||||
auto names = winrt::single_threaded_map<uint64_t, winrt::hstring>();
|
||||
std::vector<PeasantInfo> names;
|
||||
names.reserve(_peasants.size());
|
||||
|
||||
std::vector<uint64_t> peasantsToErase{};
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
const auto func = [&](const auto& id, const auto& p) -> void {
|
||||
names.push_back({ id, p.WindowName(), p.ActiveTabTitle() });
|
||||
};
|
||||
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_identifyWindows_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not identify"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
_forEachPeasant(func, onError);
|
||||
|
||||
return winrt::single_threaded_vector<PeasantInfo>(std::move(names)).GetView();
|
||||
}
|
||||
|
||||
bool Monarch::DoesQuakeWindowExist()
|
||||
{
|
||||
bool result = false;
|
||||
const auto func = [&](const auto& /*id*/, const auto& p) {
|
||||
if (p.WindowName() == QuakeWindowName)
|
||||
{
|
||||
names.Insert(id, p.WindowName());
|
||||
result = true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
peasantsToErase.push_back(id);
|
||||
}
|
||||
}
|
||||
// continue if we didn't get a positive result
|
||||
return !result;
|
||||
};
|
||||
|
||||
// Remove the dead peasants we came across while iterating.
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
_peasants.erase(id);
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_DoesQuakeWindowExist_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not ask for its name"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
|
||||
return names.GetView();
|
||||
_forEachPeasant(func, onError);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Monarch::SummonAllWindows()
|
||||
{
|
||||
auto callback = [](auto&& p, auto&& /*id*/) {
|
||||
const auto func = [&](const auto& /*id*/, const auto& p) {
|
||||
SummonWindowBehavior args{};
|
||||
args.ToggleVisibility(false);
|
||||
p.Summon(args);
|
||||
};
|
||||
auto onError = [](auto&& id) {
|
||||
|
||||
const auto onError = [&](const auto& id) {
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_SummonAll_Failed",
|
||||
TraceLoggingInt64(id, "peasantID", "The ID of the peasant which we could not summon"),
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
};
|
||||
_forAllPeasantsIgnoringTheDead(callback, onError);
|
||||
|
||||
_forEachPeasant(func, onError);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
|
||||
bool DoesQuakeWindowExist();
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
|
||||
TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs);
|
||||
TYPED_EVENT(ShowTrayIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
|
||||
|
@ -89,6 +90,67 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void _renameRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
|
||||
// Method Description:
|
||||
// - Helper for doing something on each and every peasant.
|
||||
// - We'll try calling func on every peasant.
|
||||
// - If the return type of func is void, it will perform it for all peasants.
|
||||
// - If the return type is a boolean, we'll break out of the loop once func
|
||||
// returns false.
|
||||
// - If any single peasant is dead, then we'll call onError and then add it to a
|
||||
// list of peasants to clean up once the loop ends.
|
||||
// Arguments:
|
||||
// - func: The function to call on each peasant
|
||||
// - onError: The function to call if a peasant is dead.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
template<typename F, typename T>
|
||||
void _forEachPeasant(F&& func, T&& onError)
|
||||
{
|
||||
using Map = decltype(_peasants);
|
||||
using R = std::invoke_result_t<F, Map::key_type, Map::mapped_type>;
|
||||
static constexpr auto IsVoid = std::is_void_v<R>;
|
||||
|
||||
std::vector<uint64_t> peasantsToErase;
|
||||
|
||||
for (const auto& [id, p] : _peasants)
|
||||
{
|
||||
try
|
||||
{
|
||||
if constexpr (IsVoid)
|
||||
{
|
||||
func(id, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!func(id, p))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error& exception)
|
||||
{
|
||||
onError(id);
|
||||
|
||||
if (exception.code() == 0x800706ba) // The RPC server is unavailable.
|
||||
{
|
||||
peasantsToErase.emplace_back(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& id : peasantsToErase)
|
||||
{
|
||||
_peasants.erase(id);
|
||||
_clearOldMruEntries(id);
|
||||
}
|
||||
}
|
||||
|
||||
friend class RemotingUnitTests::RemotingTests;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -31,6 +31,12 @@ namespace Microsoft.Terminal.Remoting
|
|||
Windows.Foundation.IReference<UInt64> WindowID;
|
||||
}
|
||||
|
||||
struct PeasantInfo
|
||||
{
|
||||
UInt64 Id;
|
||||
String Name;
|
||||
String TabTitle;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Monarch {
|
||||
Monarch();
|
||||
|
@ -42,7 +48,8 @@ namespace Microsoft.Terminal.Remoting
|
|||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames { get; };
|
||||
Boolean DoesQuakeWindowExist();
|
||||
Windows.Foundation.Collections.IVectorView<PeasantInfo> GetPeasantInfos { get; };
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, FindTargetWindowArgs> FindTargetWindowRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowTrayIconRequested;
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
|
||||
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs();
|
||||
WINRT_PROPERTY(winrt::hstring, WindowName);
|
||||
WINRT_PROPERTY(winrt::hstring, ActiveTabTitle);
|
||||
|
||||
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::WindowActivatedArgs);
|
||||
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs);
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace Microsoft.Terminal.Remoting
|
|||
void DisplayWindowId(); // Tells us to display its own ID (which causes a DisplayWindowIdRequested to be raised)
|
||||
|
||||
String WindowName { get; };
|
||||
String ActiveTabTitle { get; };
|
||||
void RequestIdentifyWindows(); // Tells us to raise a IdentifyWindowsRequested
|
||||
void RequestRename(RenameRequestArgs args); // Tells us to raise a RenameRequested
|
||||
void Summon(SummonWindowBehavior behavior);
|
||||
|
|
|
@ -519,11 +519,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
}
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> WindowManager::GetPeasantNames()
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> WindowManager::GetPeasantInfos()
|
||||
{
|
||||
// We should only get called when we're the monarch since the monarch
|
||||
// is the only one that knows about all peasants.
|
||||
return _monarch.GetPeasantNames();
|
||||
return _monarch.GetPeasantInfos();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -553,15 +553,11 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
|
||||
bool WindowManager::DoesQuakeWindowExist()
|
||||
{
|
||||
const auto names = GetPeasantNames();
|
||||
for (const auto [id, name] : names)
|
||||
{
|
||||
if (name == QuakeWindowName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return _monarch.DoesQuakeWindowExist();
|
||||
}
|
||||
|
||||
void WindowManager::UpdateActiveTabTitle(winrt::hstring title)
|
||||
{
|
||||
winrt::get_self<implementation::Peasant>(_peasant)->ActiveTabTitle(title);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,11 +41,12 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
|||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
|
||||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> GetPeasantNames();
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
|
||||
winrt::fire_and_forget RequestShowTrayIcon();
|
||||
winrt::fire_and_forget RequestHideTrayIcon();
|
||||
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);
|
||||
|
|
|
@ -15,8 +15,9 @@ namespace Microsoft.Terminal.Remoting
|
|||
void SummonAllWindows();
|
||||
void RequestShowTrayIcon();
|
||||
void RequestHideTrayIcon();
|
||||
void UpdateActiveTabTitle(String title);
|
||||
Boolean DoesQuakeWindowExist();
|
||||
Windows.Foundation.Collections.IMapView<UInt64, String> GetPeasantNames();
|
||||
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> ShowTrayIconRequested;
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
Color="{ThemeResource SystemErrorTextColor}" />
|
||||
|
||||
<!-- Suppress top padding -->
|
||||
<Thickness x:Key="TabViewHeaderPadding">8,0,8,0</Thickness>
|
||||
<Thickness x:Key="TabViewHeaderPadding">9,0,8,0</Thickness>
|
||||
|
||||
<!-- Remove when implementing WinUI 2.6 -->
|
||||
<Thickness x:Key="FlyoutContentPadding">12</Thickness>
|
||||
|
|
|
@ -875,6 +875,22 @@ namespace winrt::TerminalApp::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleClearBuffer(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (args)
|
||||
{
|
||||
if (const auto& realArgs = args.ActionArgs().try_as<ClearBufferArgs>())
|
||||
{
|
||||
if (const auto termControl{ _GetActiveControl() })
|
||||
{
|
||||
termControl.ClearBuffer(realArgs.Clear());
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleMultipleActions(const IInspectable& /*sender*/,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
|
|
|
@ -1478,4 +1478,9 @@ namespace winrt::TerminalApp::implementation
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AppLogic::GetShowTitleInTitlebar()
|
||||
{
|
||||
return _settings.GlobalSettings().ShowTitleInTitlebar();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ namespace winrt::TerminalApp::implementation
|
|||
|
||||
bool GetMinimizeToTray();
|
||||
bool GetAlwaysShowTrayIcon();
|
||||
bool GetShowTitleInTitlebar();
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog);
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace TerminalApp
|
|||
|
||||
Boolean GetMinimizeToTray();
|
||||
Boolean GetAlwaysShowTrayIcon();
|
||||
Boolean GetShowTitleInTitlebar();
|
||||
|
||||
FindTargetWindowResult FindTargetWindow(String[] args);
|
||||
|
||||
|
|
|
@ -1275,15 +1275,39 @@ namespace winrt::TerminalApp::implementation
|
|||
void CommandPalette::_updateRecentCommands(const hstring& command)
|
||||
{
|
||||
const auto recentCommands = ApplicationState::SharedInstance().RecentCommands();
|
||||
// If there aren't and recent commands already in the state, then we
|
||||
// don't need to copy any.
|
||||
const auto countToCopy = std::min(recentCommands ? recentCommands.Size() : 0, CommandLineHistoryLength - 1);
|
||||
std::vector<hstring> newRecentCommands{ countToCopy + 1 };
|
||||
til::at(newRecentCommands, 0) = command;
|
||||
if (countToCopy)
|
||||
// If this is the first time we've opened the commandline mode and
|
||||
// there aren't any recent commands, then just store the new command.
|
||||
if (!recentCommands)
|
||||
{
|
||||
recentCommands.GetMany(0, { newRecentCommands.data() + 1, countToCopy });
|
||||
ApplicationState::SharedInstance().RecentCommands(single_threaded_vector(std::move(std::vector{ command })));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto numNewRecentCommands = std::min(recentCommands.Size() + 1, CommandLineHistoryLength);
|
||||
|
||||
std::vector<hstring> newRecentCommands;
|
||||
newRecentCommands.reserve(numNewRecentCommands);
|
||||
|
||||
std::unordered_set<hstring> uniqueCommands;
|
||||
uniqueCommands.reserve(numNewRecentCommands);
|
||||
|
||||
newRecentCommands.push_back(command);
|
||||
uniqueCommands.insert(command);
|
||||
|
||||
for (const auto& c : recentCommands)
|
||||
{
|
||||
if (newRecentCommands.size() >= CommandLineHistoryLength)
|
||||
{
|
||||
// Don't store more than CommandLineHistoryLength commands
|
||||
break;
|
||||
}
|
||||
|
||||
if (uniqueCommands.emplace(c).second)
|
||||
{
|
||||
newRecentCommands.push_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationState::SharedInstance().RecentCommands(single_threaded_vector(std::move(newRecentCommands)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
d:DesignHeight="36"
|
||||
d:DesignHeight="40"
|
||||
d:DesignWidth="400"
|
||||
Background="Transparent"
|
||||
Orientation="Horizontal"
|
||||
|
@ -124,8 +124,9 @@
|
|||
tabs will be flush with the top of the window. See GH#2541 for
|
||||
details.
|
||||
-->
|
||||
<x:Double x:Key="CaptionButtonHeightWindowed">36.0</x:Double>
|
||||
<x:Double x:Key="CaptionButtonHeightMaximized">32.0</x:Double>
|
||||
<x:Double x:Key="CaptionButtonHeightWindowed">40.0</x:Double>
|
||||
<!-- 32 + 1 to compensate for GH#10746 -->
|
||||
<x:Double x:Key="CaptionButtonHeightMaximized">33.0</x:Double>
|
||||
|
||||
<Style x:Key="CaptionButton"
|
||||
TargetType="Button">
|
||||
|
|
|
@ -227,10 +227,11 @@ bool Pane::ResizePane(const ResizeDirection& direction)
|
|||
// Arguments:
|
||||
// - sourcePane: the pane to navigate from
|
||||
// - direction: which direction to go in
|
||||
// - mruPanes: the list of most recently used panes, in order
|
||||
// Return Value:
|
||||
// - The result of navigating from source according to direction, which may be
|
||||
// nullptr (i.e. no pane was found in that direction).
|
||||
std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> sourcePane, const FocusDirection& direction)
|
||||
std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> sourcePane, const FocusDirection& direction, const std::vector<uint32_t>& mruPanes)
|
||||
{
|
||||
// Can't navigate anywhere if we are a leaf
|
||||
if (_IsLeaf())
|
||||
|
@ -238,12 +239,23 @@ std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> source
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// If the MRU previous pane is requested we can't move; the tab handles MRU
|
||||
if (direction == FocusDirection::None || direction == FocusDirection::Previous)
|
||||
if (direction == FocusDirection::None)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Previous movement relies on the last used panes
|
||||
if (direction == FocusDirection::Previous)
|
||||
{
|
||||
// If there is actually a previous pane.
|
||||
if (mruPanes.size() > 1)
|
||||
{
|
||||
// This could return nullptr if the id is not actually in the tree.
|
||||
return FindPane(mruPanes.at(1));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if we in-order traversal is requested
|
||||
if (direction == FocusDirection::NextInOrder)
|
||||
{
|
||||
|
@ -281,11 +293,11 @@ std::shared_ptr<Pane> Pane::NavigateDirection(const std::shared_ptr<Pane> source
|
|||
// and its neighbor must necessarily be contained within the same child.
|
||||
if (!DirectionMatchesSplit(direction, _splitState))
|
||||
{
|
||||
if (auto p = _firstChild->NavigateDirection(sourcePane, direction))
|
||||
if (const auto p = _firstChild->NavigateDirection(sourcePane, direction, mruPanes))
|
||||
{
|
||||
return p;
|
||||
}
|
||||
return _secondChild->NavigateDirection(sourcePane, direction);
|
||||
return _secondChild->NavigateDirection(sourcePane, direction, mruPanes);
|
||||
}
|
||||
|
||||
// Since the direction is the same as our split, it is possible that we must
|
||||
|
@ -572,16 +584,14 @@ winrt::Windows::UI::Xaml::Controls::UserControl Pane::ReplaceControl(const winrt
|
|||
}
|
||||
|
||||
// Method Description:
|
||||
// - Given two panes, test whether the `direction` side of first is adjacent to second.
|
||||
// - Given two panes' offsets, test whether the `direction` side of first is adjacent to second.
|
||||
// Arguments:
|
||||
// - first: The reference pane.
|
||||
// - second: the pane to test adjacency with.
|
||||
// - firstOffset: The offset for the reference pane
|
||||
// - secondOffset: the offset to test adjacency with.
|
||||
// - direction: The direction to search in from the reference pane.
|
||||
// Return Value:
|
||||
// - true if the two panes are adjacent.
|
||||
bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
|
||||
const PanePoint firstOffset,
|
||||
const std::shared_ptr<Pane> second,
|
||||
bool Pane::_IsAdjacent(const PanePoint firstOffset,
|
||||
const PanePoint secondOffset,
|
||||
const FocusDirection& direction) const
|
||||
{
|
||||
|
@ -591,25 +601,11 @@ bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
|
|||
return abs(left - right) < 1e-4F;
|
||||
};
|
||||
|
||||
auto getXMax = [](PanePoint offset, std::shared_ptr<Pane> pane) {
|
||||
// If we are past startup panes should have real dimensions
|
||||
if (pane->GetRootElement().ActualWidth() > 0)
|
||||
{
|
||||
return offset.x + gsl::narrow_cast<float>(pane->GetRootElement().ActualWidth());
|
||||
}
|
||||
|
||||
// If we have simulated dimensions we rely on the calculated scale
|
||||
auto getXMax = [](PanePoint offset) {
|
||||
return offset.x + offset.scaleX;
|
||||
};
|
||||
|
||||
auto getYMax = [](PanePoint offset, std::shared_ptr<Pane> pane) {
|
||||
// If we are past startup panes should have real dimensions
|
||||
if (pane->GetRootElement().ActualHeight() > 0)
|
||||
{
|
||||
return offset.y + gsl::narrow_cast<float>(pane->GetRootElement().ActualHeight());
|
||||
}
|
||||
|
||||
// If we have simulated dimensions we rely on the calculated scale
|
||||
auto getYMax = [](PanePoint offset) {
|
||||
return offset.y + offset.scaleY;
|
||||
};
|
||||
|
||||
|
@ -619,8 +615,8 @@ bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
|
|||
// corner of the first element is within the second element's height
|
||||
if (direction == FocusDirection::Left)
|
||||
{
|
||||
auto sharesBorders = floatEqual(firstOffset.x, getXMax(secondOffset, second));
|
||||
auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < getYMax(secondOffset, second));
|
||||
const auto sharesBorders = floatEqual(firstOffset.x, getXMax(secondOffset));
|
||||
const auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < getYMax(secondOffset));
|
||||
|
||||
return sharesBorders && withinHeight;
|
||||
}
|
||||
|
@ -629,8 +625,8 @@ bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
|
|||
// corner of the first element is within the second element's height
|
||||
else if (direction == FocusDirection::Right)
|
||||
{
|
||||
auto sharesBorders = floatEqual(getXMax(firstOffset, first), secondOffset.x);
|
||||
auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < getYMax(secondOffset, second));
|
||||
const auto sharesBorders = floatEqual(getXMax(firstOffset), secondOffset.x);
|
||||
const auto withinHeight = (firstOffset.y >= secondOffset.y) && (firstOffset.y < getYMax(secondOffset));
|
||||
|
||||
return sharesBorders && withinHeight;
|
||||
}
|
||||
|
@ -639,8 +635,8 @@ bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
|
|||
// corner of the first element is within the second element's width
|
||||
else if (direction == FocusDirection::Up)
|
||||
{
|
||||
auto sharesBorders = floatEqual(firstOffset.y, getYMax(secondOffset, second));
|
||||
auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < getXMax(secondOffset, second));
|
||||
const auto sharesBorders = floatEqual(firstOffset.y, getYMax(secondOffset));
|
||||
const auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < getXMax(secondOffset));
|
||||
|
||||
return sharesBorders && withinWidth;
|
||||
}
|
||||
|
@ -649,8 +645,8 @@ bool Pane::_IsAdjacent(const std::shared_ptr<Pane> first,
|
|||
// corner of the first element is within the second element's width
|
||||
else if (direction == FocusDirection::Down)
|
||||
{
|
||||
auto sharesBorders = floatEqual(getYMax(firstOffset, first), secondOffset.y);
|
||||
auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < getXMax(secondOffset, second));
|
||||
const auto sharesBorders = floatEqual(getYMax(firstOffset), secondOffset.y);
|
||||
const auto withinWidth = (firstOffset.x >= secondOffset.x) && (firstOffset.x < getXMax(secondOffset));
|
||||
|
||||
return sharesBorders && withinWidth;
|
||||
}
|
||||
|
@ -671,51 +667,25 @@ std::pair<Pane::PanePoint, Pane::PanePoint> Pane::_GetOffsetsForPane(const PaneP
|
|||
auto firstOffset = parentOffset;
|
||||
auto secondOffset = parentOffset;
|
||||
|
||||
// When panes are initialized they don't have dimensions yet.
|
||||
if (_firstChild->GetRootElement().ActualHeight() > 0)
|
||||
// Make up fake dimensions using an exponential layout. This is useful
|
||||
// since we might need to navigate when there are panes not attached to
|
||||
// the ui tree, such as initialization, command running, and zoom.
|
||||
// Basically create the tree layout on the fly by partitioning [0,1].
|
||||
// This could run into issues if the tree depth is >127 (or other
|
||||
// degenerate splits) as a float's mantissa only has so many bits of
|
||||
// precision.
|
||||
|
||||
if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
// The second child has an offset depending on the split
|
||||
if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
const auto diff = gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualHeight());
|
||||
secondOffset.y += diff;
|
||||
// However, if a command is run in an existing window that opens multiple new panes
|
||||
// the parent will have a size (triggering this) and then the children will go
|
||||
// to the other branch.
|
||||
firstOffset.scaleY = diff;
|
||||
secondOffset.scaleY = parentOffset.scaleY - diff;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto diff = gsl::narrow_cast<float>(_firstChild->GetRootElement().ActualWidth());
|
||||
secondOffset.x += diff;
|
||||
firstOffset.scaleX = diff;
|
||||
secondOffset.scaleX = parentOffset.scaleX - diff;
|
||||
}
|
||||
secondOffset.y += (1 - _desiredSplitPosition) * parentOffset.scaleY;
|
||||
firstOffset.scaleY *= _desiredSplitPosition;
|
||||
secondOffset.scaleY *= (1 - _desiredSplitPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Since we don't have real dimensions make up fake ones using an
|
||||
// exponential layout. Basically create the tree layout on the fly by
|
||||
// partitioning [0,1].
|
||||
// This could run into issues if the tree depth is >127 (or other
|
||||
// degenerate splits) as a float's mantissa only has so many bits of
|
||||
// precision.
|
||||
|
||||
// In theory this could always be used, but there might be edge cases
|
||||
// where using the actual sizing information provides a better result.
|
||||
if (_splitState == SplitState::Horizontal)
|
||||
{
|
||||
secondOffset.y += (1 - _desiredSplitPosition) * parentOffset.scaleY;
|
||||
firstOffset.scaleY *= _desiredSplitPosition;
|
||||
secondOffset.scaleY *= (1 - _desiredSplitPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondOffset.x += (1 - _desiredSplitPosition) * parentOffset.scaleX;
|
||||
firstOffset.scaleX *= _desiredSplitPosition;
|
||||
secondOffset.scaleX *= (1 - _desiredSplitPosition);
|
||||
}
|
||||
secondOffset.x += (1 - _desiredSplitPosition) * parentOffset.scaleX;
|
||||
firstOffset.scaleX *= _desiredSplitPosition;
|
||||
secondOffset.scaleX *= (1 - _desiredSplitPosition);
|
||||
}
|
||||
|
||||
return { firstOffset, secondOffset };
|
||||
|
@ -752,7 +722,7 @@ Pane::PaneNeighborSearch Pane::_FindNeighborForPane(const FocusDirection& direct
|
|||
// If we are a leaf node test if we adjacent to the focus node
|
||||
if (_IsLeaf())
|
||||
{
|
||||
if (_IsAdjacent(searchResult.source, searchResult.sourceOffset, shared_from_this(), offset, direction))
|
||||
if (_IsAdjacent(searchResult.sourceOffset, offset, direction))
|
||||
{
|
||||
searchResult.neighbor = shared_from_this();
|
||||
}
|
||||
|
|
|
@ -76,7 +76,9 @@ public:
|
|||
void ResizeContent(const winrt::Windows::Foundation::Size& newSize);
|
||||
void Relayout();
|
||||
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
const std::vector<uint32_t>& mruPanes);
|
||||
bool SwapPanes(std::shared_ptr<Pane> first, std::shared_ptr<Pane> second);
|
||||
|
||||
std::shared_ptr<Pane> NextPane(const std::shared_ptr<Pane> pane);
|
||||
|
@ -204,7 +206,7 @@ private:
|
|||
|
||||
std::shared_ptr<Pane> _FindParentOfPane(const std::shared_ptr<Pane> pane);
|
||||
std::pair<PanePoint, PanePoint> _GetOffsetsForPane(const PanePoint parentOffset) const;
|
||||
bool _IsAdjacent(const std::shared_ptr<Pane> first, const PanePoint firstOffset, const std::shared_ptr<Pane> second, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
|
||||
bool _IsAdjacent(const PanePoint firstOffset, const PanePoint secondOffset, const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction) const;
|
||||
PaneNeighborSearch _FindNeighborForPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
PaneNeighborSearch searchResult,
|
||||
const bool focusIsSecondSide,
|
||||
|
|
|
@ -611,9 +611,13 @@ namespace winrt::TerminalApp::implementation
|
|||
tabIndex = std::clamp(tabIndex, 0u, _tabs.Size() - 1);
|
||||
|
||||
auto tab{ _tabs.GetAt(tabIndex) };
|
||||
// GH#11107 - Always just set the item directly first so that if
|
||||
// tab movement is done as part of multiple actions following calls
|
||||
// to _GetFocusedTab will return the correct tab.
|
||||
_tabView.SelectedItem(tab.TabViewItem());
|
||||
|
||||
if (_startupState == StartupState::InStartup)
|
||||
{
|
||||
_tabView.SelectedItem(tab.TabViewItem());
|
||||
_UpdatedSelectedTab(tab);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1034,7 +1034,7 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
auto newTabTitle = tab.Title();
|
||||
|
||||
if (_settings.GlobalSettings().ShowTitleInTitlebar() && tab == _GetFocusedTab())
|
||||
if (tab == _GetFocusedTab())
|
||||
{
|
||||
_TitleChangedHandlers(*this, newTabTitle);
|
||||
}
|
||||
|
@ -1179,7 +1179,6 @@ namespace winrt::TerminalApp::implementation
|
|||
{
|
||||
if (const auto terminalTab{ _GetFocusedTabImpl() })
|
||||
{
|
||||
_UnZoomIfNeeded();
|
||||
return terminalTab->NavigateFocus(direction);
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -652,26 +652,21 @@ namespace winrt::TerminalApp::implementation
|
|||
// to the terminal when no other panes are present (GH#6219)
|
||||
bool TerminalTab::NavigateFocus(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (const auto newFocus = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
|
||||
{
|
||||
if (_mruPanes.size() < 2)
|
||||
const auto res = _rootPane->FocusPane(newFocus);
|
||||
|
||||
if (_zoomedPane)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// To get to the previous pane, get the id of the previous pane and focus to that
|
||||
return _rootPane->FocusPane(_mruPanes.at(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (auto newFocus = _rootPane->NavigateDirection(_activePane, direction))
|
||||
{
|
||||
return _rootPane->FocusPane(newFocus);
|
||||
UpdateZoom(newFocus);
|
||||
}
|
||||
|
||||
return false;
|
||||
return res;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -684,27 +679,11 @@ namespace winrt::TerminalApp::implementation
|
|||
// - true if two panes were swapped.
|
||||
bool TerminalTab::SwapPane(const FocusDirection& direction)
|
||||
{
|
||||
if (direction == FocusDirection::Previous)
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
|
||||
{
|
||||
if (_mruPanes.size() < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (auto lastPane = _rootPane->FindPane(_mruPanes.at(1)))
|
||||
{
|
||||
return _rootPane->SwapPanes(_activePane, lastPane);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: This _must_ be called on the root pane, so that it can propagate
|
||||
// throughout the entire tree.
|
||||
if (auto neighbor = _rootPane->NavigateDirection(_activePane, direction))
|
||||
{
|
||||
return _rootPane->SwapPanes(_activePane, neighbor);
|
||||
}
|
||||
|
||||
return false;
|
||||
return _rootPane->SwapPanes(_activePane, neighbor);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1519,6 +1498,22 @@ namespace winrt::TerminalApp::implementation
|
|||
return _rootPane->PreCalculateCanSplit(_activePane, splitType, splitSize, availableSpace).value_or(false);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates the zoomed pane when the focus changes
|
||||
// Arguments:
|
||||
// - newFocus: the new pane to be zoomed
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::UpdateZoom(std::shared_ptr<Pane> newFocus)
|
||||
{
|
||||
// clear the existing content so the old zoomed pane can be added back to the root tree
|
||||
Content(nullptr);
|
||||
_rootPane->Restore(_zoomedPane);
|
||||
_zoomedPane = newFocus;
|
||||
_rootPane->Maximize(_zoomedPane);
|
||||
Content(_zoomedPane->GetRootElement());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Toggle our zoom state.
|
||||
// * If we're not zoomed, then zoom the active pane, making it take the
|
||||
|
|
|
@ -80,6 +80,7 @@ namespace winrt::TerminalApp::implementation
|
|||
void ResetRuntimeTabColor();
|
||||
void ActivateColorPicker();
|
||||
|
||||
void UpdateZoom(std::shared_ptr<Pane> newFocus);
|
||||
void ToggleZoom();
|
||||
bool IsZoomed();
|
||||
void EnterZoom();
|
||||
|
|
|
@ -513,6 +513,16 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
}
|
||||
}
|
||||
|
||||
void ConptyConnection::ClearBuffer()
|
||||
{
|
||||
// If we haven't connected yet, then we really don't need to do
|
||||
// anything. The connection should already start clear!
|
||||
if (_isConnected())
|
||||
{
|
||||
THROW_IF_FAILED(ConptyClearPseudoConsole(_hPC.get()));
|
||||
}
|
||||
}
|
||||
|
||||
void ConptyConnection::Close() noexcept
|
||||
try
|
||||
{
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
|||
void WriteInput(hstring const& data);
|
||||
void Resize(uint32_t rows, uint32_t columns);
|
||||
void Close() noexcept;
|
||||
void ClearBuffer();
|
||||
|
||||
winrt::guid Guid() const noexcept;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Microsoft.Terminal.TerminalConnection
|
|||
{
|
||||
ConptyConnection();
|
||||
Guid Guid { get; };
|
||||
void ClearBuffer();
|
||||
|
||||
static event NewConnectionHandler NewConnection;
|
||||
static void StartInboundListener();
|
||||
|
|
|
@ -1499,6 +1499,37 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_updatePatternLocations->Run();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Clear the contents of the buffer. The region cleared is given by
|
||||
// clearType:
|
||||
// * Screen: Clear only the contents of the visible viewport, leaving the
|
||||
// cursor row at the top of the viewport.
|
||||
// * Scrollback: Clear the contents of the scrollback.
|
||||
// * All: Do both - clear the visible viewport and the scrollback, leaving
|
||||
// only the cursor row at the top of the viewport.
|
||||
// Arguments:
|
||||
// - clearType: The type of clear to perform.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ControlCore::ClearBuffer(Control::ClearBufferType clearType)
|
||||
{
|
||||
if (clearType == Control::ClearBufferType::Scrollback || clearType == Control::ClearBufferType::All)
|
||||
{
|
||||
_terminal->EraseInDisplay(::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType::Scrollback);
|
||||
}
|
||||
|
||||
if (clearType == Control::ClearBufferType::Screen || clearType == Control::ClearBufferType::All)
|
||||
{
|
||||
// Send a signal to conpty to clear the buffer.
|
||||
if (auto conpty{ _connection.try_as<TerminalConnection::ConptyConnection>() })
|
||||
{
|
||||
// ConPTY will emit sequences to sync up our buffer with its new
|
||||
// contents.
|
||||
conpty.ClearBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hstring ControlCore::ReadEntireBuffer() const
|
||||
{
|
||||
auto terminalLock = _terminal->LockForWriting();
|
||||
|
|
|
@ -112,6 +112,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
const short wheelDelta,
|
||||
const ::Microsoft::Console::VirtualTerminal::TerminalInput::MouseButtonState state);
|
||||
void UserScrollViewport(const int viewTop);
|
||||
|
||||
void ClearBuffer(Control::ClearBufferType clearType);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void BlinkAttributeTick();
|
||||
|
|
|
@ -22,6 +22,14 @@ namespace Microsoft.Terminal.Control
|
|||
IsRightButtonDown = 0x4
|
||||
};
|
||||
|
||||
|
||||
enum ClearBufferType
|
||||
{
|
||||
Screen,
|
||||
Scrollback,
|
||||
All
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ControlCore : ICoreState
|
||||
{
|
||||
ControlCore(IControlSettings settings,
|
||||
|
@ -49,6 +57,7 @@ namespace Microsoft.Terminal.Control
|
|||
Microsoft.Terminal.Core.ControlKeyStates modifiers);
|
||||
void SendInput(String text);
|
||||
void PasteText(String text);
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
|
||||
void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
|
||||
void ClearHoveredCell();
|
||||
|
|
|
@ -239,6 +239,7 @@
|
|||
VerticalAlignment="Center"
|
||||
CornerRadius="2"
|
||||
FontSize="15"
|
||||
IsSpellCheckEnabled="False"
|
||||
KeyDown="TextBoxKeyDown"
|
||||
PlaceholderForeground="{ThemeResource TextBoxPlaceholderTextThemeBrush}" />
|
||||
|
||||
|
|
|
@ -351,6 +351,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
{
|
||||
_core.SendInput(wstr);
|
||||
}
|
||||
void TermControl::ClearBuffer(Control::ClearBufferType clearType)
|
||||
{
|
||||
_core.ClearBuffer(clearType);
|
||||
}
|
||||
|
||||
void TermControl::ToggleShaderEffects()
|
||||
{
|
||||
|
|
|
@ -63,6 +63,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
til::point GetFontSize() const;
|
||||
|
||||
void SendInput(const winrt::hstring& input);
|
||||
void ClearBuffer(Control::ClearBufferType clearType);
|
||||
|
||||
void ToggleShaderEffects();
|
||||
|
||||
winrt::fire_and_forget RenderEngineSwapChainChanged(IInspectable sender, IInspectable args);
|
||||
|
|
|
@ -6,6 +6,7 @@ import "IControlSettings.idl";
|
|||
import "IDirectKeyListener.idl";
|
||||
import "EventArgs.idl";
|
||||
import "ICoreState.idl";
|
||||
import "ControlCore.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Control
|
||||
{
|
||||
|
@ -46,6 +47,7 @@ namespace Microsoft.Terminal.Control
|
|||
|
||||
Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
|
||||
void PasteTextFromClipboard();
|
||||
void ClearBuffer(ClearBufferType clearType);
|
||||
void Close();
|
||||
Windows.Foundation.Size CharacterDimensions { get; };
|
||||
Windows.Foundation.Size MinimumSize { get; };
|
||||
|
|
|
@ -206,6 +206,7 @@
|
|||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox IsEnabled="{x:Bind local:Converters.StringsAreNotEqual('desktopWallpaper', Appearance.BackgroundImagePath), Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind local:Converters.StringFallBackToEmptyString('desktopWallpaper', Appearance.BackgroundImagePath), Mode=TwoWay, BindBack=Appearance.SetBackgroundImagePath}" />
|
||||
<Button x:Uid="Profile_BackgroundImageBrowse"
|
||||
|
|
|
@ -46,7 +46,8 @@
|
|||
|
||||
<!-- Word Delimiters -->
|
||||
<local:SettingContainer x:Uid="Globals_WordDelimiters">
|
||||
<TextBox Style="{StaticResource TextBoxSettingStyle}"
|
||||
<TextBox IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind State.Globals.WordDelimiters, Mode=TwoWay}" />
|
||||
</local:SettingContainer>
|
||||
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
SettingOverrideSource="{x:Bind State.Profile.CommandlineOverrideSource, Mode=OneWay}"
|
||||
Visibility="{x:Bind local:Converters.InvertedBooleanToVisibility(State.Profile.IsBaseLayer), Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox Style="{StaticResource TextBoxSettingStyle}"
|
||||
<TextBox IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind State.Profile.Commandline, Mode=TwoWay}" />
|
||||
<Button x:Uid="Profile_CommandlineBrowse"
|
||||
Click="Commandline_Click"
|
||||
|
@ -99,6 +100,7 @@
|
|||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox IsEnabled="{x:Bind State.Profile.UseCustomStartingDirectory, Mode=OneWay}"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind State.Profile.StartingDirectory, Mode=TwoWay}" />
|
||||
<Button x:Name="StartingDirectoryBrowse"
|
||||
|
@ -120,6 +122,7 @@
|
|||
SettingOverrideSource="{x:Bind State.Profile.IconOverrideSource, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBox FontFamily="Segoe UI, Segoe MDL2 Assets"
|
||||
IsSpellCheckEnabled="False"
|
||||
Style="{StaticResource TextBoxSettingStyle}"
|
||||
Text="{x:Bind State.Profile.Icon, Mode=TwoWay}" />
|
||||
<Button x:Uid="Profile_IconBrowse"
|
||||
|
|
|
@ -65,6 +65,7 @@ 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 ClearBufferKey{ "clearBuffer" };
|
||||
static constexpr std::string_view MultipleActionsKey{ "multipleActions" };
|
||||
|
||||
static constexpr std::string_view ActionKey{ "action" };
|
||||
|
@ -367,6 +368,7 @@ 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::ClearBuffer, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
{ ShortcutAction::MultipleActions, L"" }, // Intentionally omitted, must be generated by GenerateName
|
||||
};
|
||||
}();
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "RenameWindowArgs.g.cpp"
|
||||
#include "GlobalSummonArgs.g.cpp"
|
||||
#include "FocusPaneArgs.g.cpp"
|
||||
#include "ClearBufferArgs.g.cpp"
|
||||
#include "MultipleActionsArgs.g.cpp"
|
||||
|
||||
#include <LibraryResources.h>
|
||||
|
@ -688,6 +689,24 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
Id())
|
||||
};
|
||||
}
|
||||
winrt::hstring ClearBufferArgs::GenerateName() const
|
||||
{
|
||||
// "Clear Buffer"
|
||||
// "Clear Viewport"
|
||||
// "Clear Scrollback"
|
||||
switch (Clear())
|
||||
{
|
||||
case Control::ClearBufferType::All:
|
||||
return RS_(L"ClearAllCommandKey");
|
||||
case Control::ClearBufferType::Screen:
|
||||
return RS_(L"ClearViewportCommandKey");
|
||||
case Control::ClearBufferType::Scrollback:
|
||||
return RS_(L"ClearScrollbackCommandKey");
|
||||
}
|
||||
|
||||
// Return the empty string - the Clear() should be one of these values
|
||||
return winrt::hstring{ L"" };
|
||||
}
|
||||
|
||||
winrt::hstring MultipleActionsArgs::GenerateName() const
|
||||
{
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "RenameWindowArgs.g.h"
|
||||
#include "GlobalSummonArgs.g.h"
|
||||
#include "FocusPaneArgs.g.h"
|
||||
#include "ClearBufferArgs.g.h"
|
||||
#include "MultipleActionsArgs.g.h"
|
||||
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
|
@ -1755,6 +1756,56 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
}
|
||||
};
|
||||
|
||||
struct ClearBufferArgs : public ClearBufferArgsT<ClearBufferArgs>
|
||||
{
|
||||
ClearBufferArgs() = default;
|
||||
ClearBufferArgs(winrt::Microsoft::Terminal::Control::ClearBufferType clearType) :
|
||||
_Clear{ clearType } {};
|
||||
WINRT_PROPERTY(winrt::Microsoft::Terminal::Control::ClearBufferType, Clear, winrt::Microsoft::Terminal::Control::ClearBufferType::All);
|
||||
static constexpr std::string_view ClearKey{ "clear" };
|
||||
|
||||
public:
|
||||
hstring GenerateName() const;
|
||||
|
||||
bool Equals(const IActionArgs& other)
|
||||
{
|
||||
auto otherAsUs = other.try_as<ClearBufferArgs>();
|
||||
if (otherAsUs)
|
||||
{
|
||||
return otherAsUs->_Clear == _Clear;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static FromJsonResult FromJson(const Json::Value& json)
|
||||
{
|
||||
// LOAD BEARING: Not using make_self here _will_ break you in the future!
|
||||
auto args = winrt::make_self<ClearBufferArgs>();
|
||||
JsonUtils::GetValueForKey(json, ClearKey, args->_Clear);
|
||||
return { *args, {} };
|
||||
}
|
||||
static Json::Value ToJson(const IActionArgs& val)
|
||||
{
|
||||
if (!val)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
const auto args{ get_self<ClearBufferArgs>(val) };
|
||||
JsonUtils::SetValueForKey(json, ClearKey, args->_Clear);
|
||||
return json;
|
||||
}
|
||||
IActionArgs Copy() const
|
||||
{
|
||||
auto copy{ winrt::make_self<ClearBufferArgs>() };
|
||||
copy->_Clear = _Clear;
|
||||
return *copy;
|
||||
}
|
||||
size_t Hash() const
|
||||
{
|
||||
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Clear);
|
||||
}
|
||||
};
|
||||
|
||||
struct MultipleActionsArgs : public MultipleActionsArgsT<MultipleActionsArgs>
|
||||
{
|
||||
MultipleActionsArgs() = default;
|
||||
|
@ -1787,6 +1838,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
return {};
|
||||
}
|
||||
Json::Value json{ Json::ValueType::objectValue };
|
||||
|
||||
const auto args{ get_self<MultipleActionsArgs>(val) };
|
||||
JsonUtils::SetValueForKey(json, ActionsKey, args->_Actions);
|
||||
return json;
|
||||
|
@ -1826,5 +1878,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
|||
BASIC_FACTORY(FocusPaneArgs);
|
||||
BASIC_FACTORY(PrevTabArgs);
|
||||
BASIC_FACTORY(NextTabArgs);
|
||||
BASIC_FACTORY(ClearBufferArgs);
|
||||
BASIC_FACTORY(MultipleActionsArgs);
|
||||
}
|
||||
|
|
|
@ -311,9 +311,15 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
UInt32 Id { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass ClearBufferArgs : IActionArgs
|
||||
{
|
||||
ClearBufferArgs(Microsoft.Terminal.Control.ClearBufferType clear);
|
||||
Microsoft.Terminal.Control.ClearBufferType Clear { get; };
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass MultipleActionsArgs : IActionArgs
|
||||
{
|
||||
MultipleActionsArgs();
|
||||
Windows.Foundation.Collections.IVector<ActionAndArgs> Actions;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
ON_ALL_ACTIONS(GlobalSummon) \
|
||||
ON_ALL_ACTIONS(QuakeMode) \
|
||||
ON_ALL_ACTIONS(FocusPane) \
|
||||
ON_ALL_ACTIONS(ClearBuffer) \
|
||||
ON_ALL_ACTIONS(MultipleActions)
|
||||
|
||||
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
|
||||
|
@ -111,4 +112,5 @@
|
|||
ON_ALL_ACTIONS_WITH_ARGS(SwitchToTab) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(ToggleCommandPalette) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(FocusPane) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer) \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(MultipleActions)
|
||||
|
|
|
@ -446,6 +446,18 @@
|
|||
<value>Focus pane {0}</value>
|
||||
<comment>{0} will be replaced with a user-specified number</comment>
|
||||
</data>
|
||||
<data name="ClearAllCommandKey" xml:space="preserve">
|
||||
<value>Clear Buffer</value>
|
||||
<comment>A command to clear the entirety of the Terminal output buffer</comment>
|
||||
</data>
|
||||
<data name="ClearViewportCommandKey" xml:space="preserve">
|
||||
<value>Clear Viewport</value>
|
||||
<comment>A command to clear the active viewport of the Terminal</comment>
|
||||
</data>
|
||||
<data name="ClearScrollbackCommandKey" xml:space="preserve">
|
||||
<value>Clear Scrollback</value>
|
||||
<comment>A command to clear the part of the buffer above the viewport</comment>
|
||||
</data>
|
||||
<data name="InboxWindowsConsoleAuthor" xml:space="preserve">
|
||||
<value>Microsoft Corporation</value>
|
||||
<comment>Paired with `InboxWindowsConsoleName`, this is the application author... which is us: Microsoft.</comment>
|
||||
|
|
|
@ -472,6 +472,15 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::MonitorBehavior)
|
|||
};
|
||||
};
|
||||
|
||||
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ClearBufferType)
|
||||
{
|
||||
JSON_MAPPINGS(3) = {
|
||||
pair_type{ "all", ValueType::All },
|
||||
pair_type{ "screen", ValueType::Screen },
|
||||
pair_type{ "scrollback", ValueType::Scrollback },
|
||||
};
|
||||
};
|
||||
|
||||
JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::IntenseStyle)
|
||||
{
|
||||
static constexpr std::array<pair_type, 4> mappings = {
|
||||
|
@ -479,5 +488,6 @@ JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::IntenseStyle)
|
|||
pair_type{ "bold", ValueType::Bold },
|
||||
pair_type{ "bright", ValueType::Bright },
|
||||
pair_type{ "all", AllSet },
|
||||
|
||||
};
|
||||
};
|
||||
|
|
|
@ -383,6 +383,7 @@
|
|||
{ "command": "scrollUpPage", "keys": "ctrl+shift+pgup" },
|
||||
{ "command": "scrollToTop", "keys": "ctrl+shift+home" },
|
||||
{ "command": "scrollToBottom", "keys": "ctrl+shift+end" },
|
||||
{ "command": { "action": "clearBuffer", "clear": "all" } },
|
||||
|
||||
// Visual Adjustments
|
||||
{ "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+plus" },
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "../TerminalControl/ControlCore.h"
|
||||
#include "MockControlSettings.h"
|
||||
#include "MockConnection.h"
|
||||
#include "../UnitTests_TerminalCore/TestUtils.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
using namespace WEX::Logging;
|
||||
|
@ -32,6 +33,10 @@ namespace ControlUnitTests
|
|||
|
||||
TEST_METHOD(TestFontInitializedInCtor);
|
||||
|
||||
TEST_METHOD(TestClearScrollback);
|
||||
TEST_METHOD(TestClearScreen);
|
||||
TEST_METHOD(TestClearAll);
|
||||
|
||||
TEST_CLASS_SETUP(ModuleSetup)
|
||||
{
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
|
@ -66,6 +71,15 @@ namespace ControlUnitTests
|
|||
core->_inUnitTests = true;
|
||||
return core;
|
||||
}
|
||||
|
||||
void _standardInit(winrt::com_ptr<Control::implementation::ControlCore> core)
|
||||
{
|
||||
// "Consolas" ends up with an actual size of 9x21 at 96DPI. So
|
||||
// let's just arbitrarily start with a 270x420px (30x20 chars) window
|
||||
core->Initialize(270, 420, 1.0);
|
||||
VERIFY_IS_TRUE(core->_initializedTerminal);
|
||||
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
|
||||
}
|
||||
};
|
||||
|
||||
void ControlCoreTests::ComPtrSettings()
|
||||
|
@ -202,4 +216,122 @@ namespace ControlUnitTests
|
|||
VERIFY_ARE_EQUAL(L"Impact", std::wstring_view{ core->_actualFont.GetFaceName() });
|
||||
}
|
||||
|
||||
void ControlCoreTests::TestClearScrollback()
|
||||
{
|
||||
auto [settings, conn] = _createSettingsAndConnection();
|
||||
Log::Comment(L"Create ControlCore object");
|
||||
auto core = winrt::make_self<Control::implementation::ControlCore>(*settings, *conn);
|
||||
VERIFY_IS_NOT_NULL(core);
|
||||
_standardInit(core);
|
||||
|
||||
Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' "
|
||||
L"(leaving the cursor afer 'Bar')");
|
||||
for (int i = 0; i < 40; ++i)
|
||||
{
|
||||
conn->WriteInput(L"Foo\r\n");
|
||||
}
|
||||
conn->WriteInput(L"Bar");
|
||||
|
||||
// We printed that 40 times, but the final \r\n bumped the view down one MORE row.
|
||||
Log::Comment(L"Check the buffer viewport before the clear");
|
||||
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
|
||||
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
|
||||
VERIFY_ARE_EQUAL(20, core->ViewHeight());
|
||||
VERIFY_ARE_EQUAL(41, core->BufferHeight());
|
||||
|
||||
Log::Comment(L"Clear the buffer");
|
||||
core->ClearBuffer(Control::ClearBufferType::Scrollback);
|
||||
|
||||
Log::Comment(L"Check the buffer after the clear");
|
||||
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
|
||||
VERIFY_ARE_EQUAL(0, core->ScrollOffset());
|
||||
VERIFY_ARE_EQUAL(20, core->ViewHeight());
|
||||
VERIFY_ARE_EQUAL(20, core->BufferHeight());
|
||||
|
||||
// In this test, we can't actually check if we cleared the buffer
|
||||
// contents. ConPTY will handle the actual clearing of the buffer
|
||||
// contents. We can only ensure that the viewport moved when we did a
|
||||
// clear scrollback.
|
||||
//
|
||||
// The ConptyRoundtripTests test the actual clearing of the contents.
|
||||
}
|
||||
void ControlCoreTests::TestClearScreen()
|
||||
{
|
||||
auto [settings, conn] = _createSettingsAndConnection();
|
||||
Log::Comment(L"Create ControlCore object");
|
||||
auto core = winrt::make_self<Control::implementation::ControlCore>(*settings, *conn);
|
||||
VERIFY_IS_NOT_NULL(core);
|
||||
_standardInit(core);
|
||||
|
||||
Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' "
|
||||
L"(leaving the cursor afer 'Bar')");
|
||||
for (int i = 0; i < 40; ++i)
|
||||
{
|
||||
conn->WriteInput(L"Foo\r\n");
|
||||
}
|
||||
conn->WriteInput(L"Bar");
|
||||
|
||||
// We printed that 40 times, but the final \r\n bumped the view down one MORE row.
|
||||
Log::Comment(L"Check the buffer viewport before the clear");
|
||||
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
|
||||
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
|
||||
VERIFY_ARE_EQUAL(20, core->ViewHeight());
|
||||
VERIFY_ARE_EQUAL(41, core->BufferHeight());
|
||||
|
||||
Log::Comment(L"Clear the buffer");
|
||||
core->ClearBuffer(Control::ClearBufferType::Screen);
|
||||
|
||||
Log::Comment(L"Check the buffer after the clear");
|
||||
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
|
||||
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
|
||||
VERIFY_ARE_EQUAL(20, core->ViewHeight());
|
||||
VERIFY_ARE_EQUAL(41, core->BufferHeight());
|
||||
|
||||
// In this test, we can't actually check if we cleared the buffer
|
||||
// contents. ConPTY will handle the actual clearing of the buffer
|
||||
// contents. We can only ensure that the viewport moved when we did a
|
||||
// clear scrollback.
|
||||
//
|
||||
// The ConptyRoundtripTests test the actual clearing of the contents.
|
||||
}
|
||||
void ControlCoreTests::TestClearAll()
|
||||
{
|
||||
auto [settings, conn] = _createSettingsAndConnection();
|
||||
Log::Comment(L"Create ControlCore object");
|
||||
auto core = winrt::make_self<Control::implementation::ControlCore>(*settings, *conn);
|
||||
VERIFY_IS_NOT_NULL(core);
|
||||
_standardInit(core);
|
||||
|
||||
Log::Comment(L"Print 40 rows of 'Foo', and a single row of 'Bar' "
|
||||
L"(leaving the cursor afer 'Bar')");
|
||||
for (int i = 0; i < 40; ++i)
|
||||
{
|
||||
conn->WriteInput(L"Foo\r\n");
|
||||
}
|
||||
conn->WriteInput(L"Bar");
|
||||
|
||||
// We printed that 40 times, but the final \r\n bumped the view down one MORE row.
|
||||
Log::Comment(L"Check the buffer viewport before the clear");
|
||||
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
|
||||
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
|
||||
VERIFY_ARE_EQUAL(20, core->ViewHeight());
|
||||
VERIFY_ARE_EQUAL(41, core->BufferHeight());
|
||||
|
||||
Log::Comment(L"Clear the buffer");
|
||||
core->ClearBuffer(Control::ClearBufferType::All);
|
||||
|
||||
Log::Comment(L"Check the buffer after the clear");
|
||||
VERIFY_ARE_EQUAL(20, core->_terminal->GetViewport().Height());
|
||||
VERIFY_ARE_EQUAL(0, core->ScrollOffset());
|
||||
VERIFY_ARE_EQUAL(20, core->ViewHeight());
|
||||
VERIFY_ARE_EQUAL(20, core->BufferHeight());
|
||||
|
||||
// In this test, we can't actually check if we cleared the buffer
|
||||
// contents. ConPTY will handle the actual clearing of the buffer
|
||||
// contents. We can only ensure that the viewport moved when we did a
|
||||
// clear scrollback.
|
||||
//
|
||||
// The ConptyRoundtripTests test the actual clearing of the contents.
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ namespace RemotingUnitTests
|
|||
void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; };
|
||||
uint64_t GetID() { throw winrt::hresult_error{}; };
|
||||
winrt::hstring WindowName() { throw winrt::hresult_error{}; };
|
||||
winrt::hstring ActiveTabTitle() { throw winrt::hresult_error{}; };
|
||||
void ActiveTabTitle(const winrt::hstring& /*value*/) { throw winrt::hresult_error{}; };
|
||||
uint64_t GetPID() { throw winrt::hresult_error{}; };
|
||||
bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; }
|
||||
void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { throw winrt::hresult_error{}; }
|
||||
|
|
|
@ -218,10 +218,13 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final
|
|||
|
||||
TEST_METHOD(ResizeInitializeBufferWithDefaultAttrs);
|
||||
|
||||
TEST_METHOD(ClearBufferSignal);
|
||||
|
||||
private:
|
||||
bool _writeCallback(const char* const pch, size_t const cch);
|
||||
void _flushFirstFrame();
|
||||
void _resizeConpty(const unsigned short sx, const unsigned short sy);
|
||||
void _clearConpty();
|
||||
|
||||
[[nodiscard]] std::tuple<TextBuffer*, TextBuffer*> _performResize(const til::size& newSize);
|
||||
|
||||
|
@ -297,6 +300,12 @@ void ConptyRoundtripTests::_resizeConpty(const unsigned short sx,
|
|||
}
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::_clearConpty()
|
||||
{
|
||||
// Taken verbatim from implementation in PtySignalInputThread::_DoClearBuffer
|
||||
_pConApi->PrivateClearBuffer();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::tuple<TextBuffer*, TextBuffer*> ConptyRoundtripTests::_performResize(const til::size& newSize)
|
||||
{
|
||||
// IMPORTANT! Anyone calling this should make sure that the test is running
|
||||
|
@ -3675,3 +3684,77 @@ void ConptyRoundtripTests::HyperlinkIdConsistency()
|
|||
verifyData(hostTb);
|
||||
verifyData(termTb);
|
||||
}
|
||||
|
||||
void ConptyRoundtripTests::ClearBufferSignal()
|
||||
{
|
||||
Log::Comment(L"Write some text to the conpty buffer. Send a ClearBuffer "
|
||||
L"signal, and check that all but the cursor line is removed "
|
||||
L"from the host and the terminal.");
|
||||
auto& g = ServiceLocator::LocateGlobals();
|
||||
auto& renderer = *g.pRender;
|
||||
auto& gci = g.getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer();
|
||||
auto& sm = si.GetStateMachine();
|
||||
auto* hostTb = &si.GetTextBuffer();
|
||||
auto* termTb = term->_buffer.get();
|
||||
|
||||
_flushFirstFrame();
|
||||
|
||||
_checkConptyOutput = false;
|
||||
_logConpty = true;
|
||||
|
||||
// Print two lines of text:
|
||||
// |AAAAAAAAAAAAA BBBBBB| <wrap>
|
||||
// |BBBBBBBB_ | <break>
|
||||
// (cursor on the '_')
|
||||
// A's are in blue-on-green,
|
||||
// B's are in red-on-yellow
|
||||
|
||||
sm.ProcessString(L"\x1b[?25l");
|
||||
sm.ProcessString(L"\x1b[?34;42m");
|
||||
sm.ProcessString(std::wstring(50, L'A'));
|
||||
sm.ProcessString(L" ");
|
||||
sm.ProcessString(L"\x1b[?31;43m");
|
||||
sm.ProcessString(std::wstring(50, L'B'));
|
||||
sm.ProcessString(L"\x1b[?m");
|
||||
sm.ProcessString(L"\x1b[?25h");
|
||||
|
||||
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport, const bool before) {
|
||||
const short width = viewport.width<short>();
|
||||
const short numCharsOnSecondLine = 50 - (width - 51);
|
||||
auto iter1 = tb.GetCellDataAt({ 0, 0 });
|
||||
if (before)
|
||||
{
|
||||
TestUtils::VerifySpanOfText(L"A", iter1, 0, 50);
|
||||
TestUtils::VerifySpanOfText(L" ", iter1, 0, 1);
|
||||
TestUtils::VerifySpanOfText(L"B", iter1, 0, 50);
|
||||
COORD expectedCursor{ numCharsOnSecondLine, 1 };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, tb.GetCursor().GetPosition());
|
||||
}
|
||||
else
|
||||
{
|
||||
TestUtils::VerifySpanOfText(L"B", iter1, 0, numCharsOnSecondLine);
|
||||
COORD expectedCursor{ numCharsOnSecondLine, 0 };
|
||||
VERIFY_ARE_EQUAL(expectedCursor, tb.GetCursor().GetPosition());
|
||||
}
|
||||
};
|
||||
|
||||
Log::Comment(L"========== Checking the host buffer state (before) ==========");
|
||||
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), true);
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
Log::Comment(L"========== Checking the terminal buffer state (before) ==========");
|
||||
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), true);
|
||||
|
||||
Log::Comment(L"========== Clear the ConPTY buffer with the signal ==========");
|
||||
_clearConpty();
|
||||
|
||||
Log::Comment(L"========== Checking the host buffer state (after) ==========");
|
||||
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), false);
|
||||
|
||||
Log::Comment(L"Painting the frame");
|
||||
VERIFY_SUCCEEDED(renderer.PaintFrame());
|
||||
Log::Comment(L"========== Checking the terminal buffer state (after) ==========");
|
||||
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), false);
|
||||
}
|
||||
|
|
|
@ -299,8 +299,9 @@ void AppHost::Initialize()
|
|||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when the app's title changes. Fires off a window message so we can
|
||||
// update the window's title on the main thread.
|
||||
// - Called everytime when the active tab's title changes. We'll also fire off
|
||||
// a window message so we can update the window's title on the main thread,
|
||||
// though we'll only do so if the settings are configured for that.
|
||||
// Arguments:
|
||||
// - sender: unused
|
||||
// - newTitle: the string to use as the new window title
|
||||
|
@ -308,7 +309,11 @@ void AppHost::Initialize()
|
|||
// - <none>
|
||||
void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle)
|
||||
{
|
||||
_window->UpdateTitle(newTitle);
|
||||
if (_logic.GetShowTitleInTitlebar())
|
||||
{
|
||||
_window->UpdateTitle(newTitle);
|
||||
}
|
||||
_windowManager.UpdateActiveTabTitle(newTitle);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
|
@ -1031,7 +1036,7 @@ void AppHost::_CreateTrayIcon()
|
|||
// Hookup the handlers, save the tokens for revoking if settings change.
|
||||
_ReAddTrayIconToken = _window->NotifyReAddTrayIcon([this]() { _trayIcon->ReAddTrayIcon(); });
|
||||
_TrayIconPressedToken = _window->NotifyTrayIconPressed([this]() { _trayIcon->TrayIconPressed(); });
|
||||
_ShowTrayContextMenuToken = _window->NotifyShowTrayContextMenu([this](til::point coord) { _trayIcon->ShowTrayContextMenu(coord, _windowManager.GetPeasantNames()); });
|
||||
_ShowTrayContextMenuToken = _window->NotifyShowTrayContextMenu([this](til::point coord) { _trayIcon->ShowTrayContextMenu(coord, _windowManager.GetPeasantInfos()); });
|
||||
_TrayMenuItemSelectedToken = _window->NotifyTrayMenuItemSelected([this](HMENU hm, UINT idx) { _trayIcon->TrayMenuItemSelected(hm, idx); });
|
||||
_trayIcon->SummonWindowRequested([this](auto& args) { _windowManager.SummonWindow(args); });
|
||||
}
|
||||
|
|
|
@ -109,8 +109,8 @@ void TrayIcon::CreateTrayIcon()
|
|||
// - peasants: The map of all peasants that should be available in the context menu.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TrayIcon::ShowTrayContextMenu(const til::point coord,
|
||||
IMapView<uint64_t, winrt::hstring> peasants)
|
||||
void TrayIcon::ShowTrayContextMenu(const til::point& coord,
|
||||
const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
|
||||
{
|
||||
if (const auto hMenu = _CreateTrayContextMenu(peasants))
|
||||
{
|
||||
|
@ -142,7 +142,7 @@ void TrayIcon::ShowTrayContextMenu(const til::point coord,
|
|||
// - peasants: A map of all peasants' ID to their window name.
|
||||
// Return Value:
|
||||
// - The handle to the newly created context menu.
|
||||
HMENU TrayIcon::_CreateTrayContextMenu(IMapView<uint64_t, winrt::hstring> peasants)
|
||||
HMENU TrayIcon::_CreateTrayContextMenu(const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
|
||||
{
|
||||
auto hMenu = CreatePopupMenu();
|
||||
if (hMenu)
|
||||
|
@ -161,17 +161,22 @@ HMENU TrayIcon::_CreateTrayContextMenu(IMapView<uint64_t, winrt::hstring> peasan
|
|||
// Submenu for Windows
|
||||
if (auto submenu = CreatePopupMenu())
|
||||
{
|
||||
const auto locWindow = RS_(L"WindowIdLabel");
|
||||
const auto locUnnamed = RS_(L"UnnamedWindowName");
|
||||
for (const auto [id, name] : peasants)
|
||||
for (const auto& p : peasants)
|
||||
{
|
||||
winrt::hstring displayText = name;
|
||||
if (name.empty())
|
||||
std::wstringstream displayText;
|
||||
displayText << L"#" << p.Id;
|
||||
|
||||
if (!p.TabTitle.empty())
|
||||
{
|
||||
displayText = fmt::format(L"{} {} - <{}>", locWindow, id, locUnnamed);
|
||||
displayText << L": " << std::wstring_view{ p.TabTitle };
|
||||
}
|
||||
|
||||
AppendMenu(submenu, MF_STRING, gsl::narrow<UINT_PTR>(id), displayText.c_str());
|
||||
if (!p.Name.empty())
|
||||
{
|
||||
displayText << L" [" << std::wstring_view{ p.Name } << L"]";
|
||||
}
|
||||
|
||||
AppendMenu(submenu, MF_STRING, gsl::narrow<UINT_PTR>(p.Id), displayText.str().c_str());
|
||||
}
|
||||
|
||||
MENUINFO submenuInfo{};
|
||||
|
|
|
@ -24,14 +24,14 @@ public:
|
|||
void ReAddTrayIcon();
|
||||
|
||||
void TrayIconPressed();
|
||||
void ShowTrayContextMenu(const til::point coord, winrt::Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> peasants);
|
||||
void ShowTrayContextMenu(const til::point& coord, const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
|
||||
void TrayMenuItemSelected(const HMENU menu, const UINT menuItemIndex);
|
||||
|
||||
WINRT_CALLBACK(SummonWindowRequested, winrt::delegate<void(winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs)>);
|
||||
|
||||
private:
|
||||
void _CreateWindow();
|
||||
HMENU _CreateTrayContextMenu(winrt::Windows::Foundation::Collections::IMapView<uint64_t, winrt::hstring> peasants);
|
||||
HMENU _CreateTrayContextMenu(const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
|
||||
|
||||
wil::unique_hwnd _trayIconHwnd;
|
||||
HWND _owningHwnd;
|
||||
|
|
|
@ -82,6 +82,21 @@ void PtySignalInputThread::ConnectConsole() noexcept
|
|||
{
|
||||
switch (signalId)
|
||||
{
|
||||
case PtySignal::ClearBuffer:
|
||||
{
|
||||
LockConsole();
|
||||
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
|
||||
|
||||
// If the client app hasn't yet connected, stash the new size in the launchArgs.
|
||||
// We'll later use the value in launchArgs to set up the console buffer
|
||||
// We must be under lock here to ensure that someone else doesn't come in
|
||||
// and set with `ConnectConsole` while we're looking and modifying this.
|
||||
if (_consoleConnected)
|
||||
{
|
||||
_DoClearBuffer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PtySignal::ResizeWindow:
|
||||
{
|
||||
ResizeWindowData resizeMsg = { 0 };
|
||||
|
@ -128,6 +143,11 @@ void PtySignalInputThread::_DoResizeWindow(const ResizeWindowData& data)
|
|||
}
|
||||
}
|
||||
|
||||
void PtySignalInputThread::_DoClearBuffer()
|
||||
{
|
||||
_pConApi->PrivateClearBuffer();
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves bytes from the file stream and exits or throws errors should the pipe state
|
||||
// be compromised.
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace Microsoft::Console
|
|||
private:
|
||||
enum class PtySignal : unsigned short
|
||||
{
|
||||
ClearBuffer = 2,
|
||||
ResizeWindow = 8
|
||||
};
|
||||
|
||||
|
@ -47,6 +48,7 @@ namespace Microsoft::Console
|
|||
[[nodiscard]] HRESULT _InputThread();
|
||||
bool _GetData(_Out_writes_bytes_(cbBuffer) void* const pBuffer, const DWORD cbBuffer);
|
||||
void _DoResizeWindow(const ResizeWindowData& data);
|
||||
void _DoClearBuffer();
|
||||
void _Shutdown();
|
||||
|
||||
wil::unique_hfile _hFile;
|
||||
|
|
|
@ -1609,6 +1609,12 @@ void DoSrvPrivateEnableAlternateScroll(const bool fEnable)
|
|||
return screenInfo.GetActiveBuffer().VtEraseAll();
|
||||
}
|
||||
|
||||
// See SCREEN_INFORMATION::ClearBuffer's description for details.
|
||||
[[nodiscard]] HRESULT DoSrvPrivateClearBuffer(SCREEN_INFORMATION& screenInfo)
|
||||
{
|
||||
return screenInfo.GetActiveBuffer().ClearBuffer();
|
||||
}
|
||||
|
||||
void DoSrvSetCursorStyle(SCREEN_INFORMATION& screenInfo,
|
||||
const CursorType cursorType)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,7 @@ void DoSrvPrivateEnableAnyEventMouseMode(const bool fEnable);
|
|||
void DoSrvPrivateEnableAlternateScroll(const bool fEnable);
|
||||
|
||||
[[nodiscard]] HRESULT DoSrvPrivateEraseAll(SCREEN_INFORMATION& screenInfo);
|
||||
[[nodiscard]] HRESULT DoSrvPrivateClearBuffer(SCREEN_INFORMATION& screenInfo);
|
||||
|
||||
void DoSrvSetCursorStyle(SCREEN_INFORMATION& screenInfo,
|
||||
const CursorType cursorType);
|
||||
|
|
|
@ -545,6 +545,11 @@ bool ConhostInternalGetSet::PrivateEraseAll()
|
|||
return SUCCEEDED(DoSrvPrivateEraseAll(_io.GetActiveOutputBuffer()));
|
||||
}
|
||||
|
||||
bool ConhostInternalGetSet::PrivateClearBuffer()
|
||||
{
|
||||
return SUCCEEDED(DoSrvPrivateClearBuffer(_io.GetActiveOutputBuffer()));
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Retrieves the current user default cursor style.
|
||||
// Arguments:
|
||||
|
|
|
@ -104,6 +104,7 @@ public:
|
|||
bool PrivateEnableAnyEventMouseMode(const bool enabled) override;
|
||||
bool PrivateEnableAlternateScroll(const bool enabled) override;
|
||||
bool PrivateEraseAll() override;
|
||||
bool PrivateClearBuffer() override;
|
||||
|
||||
bool GetUserDefaultCursorStyle(CursorType& style) override;
|
||||
bool SetCursorStyle(CursorType const style) override;
|
||||
|
|
|
@ -2277,6 +2277,55 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport,
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Clear the entire contents of the viewport, except for the cursor's row,
|
||||
// which is moved to the top line of the viewport.
|
||||
// - This is used exclusively by ConPTY to support GH#1193, GH#1882. This allows
|
||||
// a terminal to clear the contents of the ConPTY buffer, which is important
|
||||
// if the user would like to be able to clear the terminal-side buffer.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT SCREEN_INFORMATION::ClearBuffer()
|
||||
{
|
||||
const COORD oldCursorPos = _textBuffer->GetCursor().GetPosition();
|
||||
short sNewTop = oldCursorPos.Y;
|
||||
const Viewport oldViewport = _viewport;
|
||||
|
||||
short delta = (sNewTop + _viewport.Height()) - (GetBufferSize().Height());
|
||||
for (auto i = 0; i < delta; i++)
|
||||
{
|
||||
_textBuffer->IncrementCircularBuffer();
|
||||
sNewTop--;
|
||||
}
|
||||
|
||||
const COORD coordNewOrigin = { 0, sNewTop };
|
||||
RETURN_IF_FAILED(SetViewportOrigin(true, coordNewOrigin, true));
|
||||
|
||||
// Place the cursor at the same x coord, on the row that's now the top
|
||||
RETURN_IF_FAILED(SetCursorPosition(COORD{ oldCursorPos.X, sNewTop }, false));
|
||||
|
||||
// Update all the rows in the current viewport with the standard erase attributes,
|
||||
// i.e. the current background color, but with no meta attributes set.
|
||||
auto fillAttributes = GetAttributes();
|
||||
fillAttributes.SetStandardErase();
|
||||
|
||||
// +1 on the y coord because we don't want to clear the attributes of the
|
||||
// cursor row, the one we saved.
|
||||
auto fillPosition = COORD{ 0, _viewport.Top() + 1 };
|
||||
auto fillLength = gsl::narrow_cast<size_t>(_viewport.Height() * GetBufferSize().Width());
|
||||
auto fillData = OutputCellIterator{ fillAttributes, fillLength };
|
||||
Write(fillData, fillPosition, false);
|
||||
|
||||
_textBuffer->GetRenderTarget().TriggerRedrawAll();
|
||||
|
||||
// Also reset the line rendition for the erased rows.
|
||||
_textBuffer->ResetLineRenditionRange(_viewport.Top(), _viewport.BottomExclusive());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Sets up the Output state machine to be in pty mode. Sequences it doesn't
|
||||
// understand will be written to the pTtyConnection passed in here.
|
||||
|
|
|
@ -222,6 +222,7 @@ public:
|
|||
const TextAttribute& popupAttributes);
|
||||
|
||||
[[nodiscard]] HRESULT VtEraseAll();
|
||||
[[nodiscard]] HRESULT ClearBuffer();
|
||||
|
||||
void SetTerminalConnection(_In_ Microsoft::Console::ITerminalOutputConnection* const pTtyConnection);
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ HRESULT WINAPI ConptyCreatePseudoConsole(COORD size, HANDLE hInput, HANDLE hOutp
|
|||
|
||||
HRESULT WINAPI ConptyResizePseudoConsole(HPCON hPC, COORD size);
|
||||
|
||||
HRESULT WINAPI ConptyClearPseudoConsole(HPCON hPC);
|
||||
|
||||
VOID WINAPI ConptyClosePseudoConsole(HPCON hPC);
|
||||
|
||||
HRESULT WINAPI ConptyPackPseudoConsole(HANDLE hServerProcess, HANDLE hRef, HANDLE hSignal, HPCON* phPC);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <memory>
|
||||
#pragma once
|
||||
|
||||
const unsigned int PTY_SIGNAL_CLEAR_WINDOW = 2u;
|
||||
const unsigned int PTY_SIGNAL_RESIZE_WINDOW = 8u;
|
||||
|
||||
HRESULT CreateConPty(const std::wstring& cmdline, // _In_
|
||||
|
|
|
@ -73,6 +73,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
virtual bool PrivateEnableAnyEventMouseMode(const bool enabled) = 0;
|
||||
virtual bool PrivateEnableAlternateScroll(const bool enabled) = 0;
|
||||
virtual bool PrivateEraseAll() = 0;
|
||||
virtual bool PrivateClearBuffer() = 0;
|
||||
virtual bool GetUserDefaultCursorStyle(CursorType& style) = 0;
|
||||
virtual bool SetCursorStyle(const CursorType style) = 0;
|
||||
virtual bool SetCursorColor(const COLORREF color) = 0;
|
||||
|
|
|
@ -418,6 +418,12 @@ public:
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
bool PrivateClearBuffer() override
|
||||
{
|
||||
Log::Comment(L"PrivateClearBuffer MOCK called...");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool GetUserDefaultCursorStyle(CursorType& style) override
|
||||
{
|
||||
style = CursorType::Legacy;
|
||||
|
|
|
@ -14,8 +14,6 @@ terminal and the console without a lot of work.
|
|||
This script has both some "simple" emoji - burrito, cheese, etc. and some more
|
||||
complex ones - WOMAN COOK is actually two emoji with a zero width joiner.
|
||||
"""
|
||||
import sys
|
||||
import time # time.sleep is in seconds
|
||||
from common import *
|
||||
|
||||
# Run this file with:
|
||||
|
|
|
@ -2,3 +2,4 @@ EXPORTS
|
|||
CreatePseudoConsole = ConptyCreatePseudoConsole
|
||||
ResizePseudoConsole = ConptyResizePseudoConsole
|
||||
ClosePseudoConsole = ConptyClosePseudoConsole
|
||||
ClearPseudoConsole = ConptyClearPseudoConsole
|
||||
|
|
|
@ -231,6 +231,27 @@ HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const CO
|
|||
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Clears the conpty
|
||||
// Arguments:
|
||||
// - hSignal: A signal pipe as returned by CreateConPty.
|
||||
// Return Value:
|
||||
// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
|
||||
// write the clear message to the pty.
|
||||
HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty)
|
||||
{
|
||||
if (pPty == nullptr)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
unsigned short signalPacket[1];
|
||||
signalPacket[0] = PTY_SIGNAL_CLEAR_WINDOW;
|
||||
|
||||
const BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
|
||||
return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - This closes each of the members of a PseudoConsole. It does not free the
|
||||
// data associated with the PseudoConsole. This is helpful for testing,
|
||||
|
@ -385,6 +406,23 @@ extern "C" HRESULT WINAPI ConptyResizePseudoConsole(_In_ HPCON hPC, _In_ COORD s
|
|||
return hr;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// - Clear the contents of the conpty buffer, leaving the cursor row at the top
|
||||
// of the viewport.
|
||||
// - This is used exclusively by ConPTY to support GH#1193, GH#1882. This allows
|
||||
// a terminal to clear the contents of the ConPTY buffer, which is important
|
||||
// if the user would like to be able to clear the terminal-side buffer.
|
||||
extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC)
|
||||
{
|
||||
const PseudoConsole* const pPty = (PseudoConsole*)hPC;
|
||||
HRESULT hr = pPty == nullptr ? E_INVALIDARG : S_OK;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = _ClearPseudoConsole(pPty);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Function Description:
|
||||
// Closes the conpty and all associated state.
|
||||
// Client applications attached to the conpty will also behave as though the
|
||||
|
|
|
@ -17,6 +17,7 @@ typedef struct _PseudoConsole
|
|||
// Signals
|
||||
// These are not defined publicly, but are used for controlling the conpty via
|
||||
// the signal pipe.
|
||||
#define PTY_SIGNAL_CLEAR_WINDOW (2u)
|
||||
#define PTY_SIGNAL_RESIZE_WINDOW (8u)
|
||||
|
||||
// CreatePseudoConsole Flags
|
||||
|
@ -34,6 +35,7 @@ HRESULT _CreatePseudoConsole(const HANDLE hToken,
|
|||
_Inout_ PseudoConsole* pPty);
|
||||
|
||||
HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size);
|
||||
HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty);
|
||||
void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty);
|
||||
VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty);
|
||||
|
||||
|
|
Loading…
Reference in a new issue