I really shouldn't be doing the throttledfunc thing in this PR

This commit is contained in:
Mike Griese 2021-05-25 16:30:51 -05:00
parent d615ece8c8
commit 04b751faa7
5 changed files with 68 additions and 102 deletions

View file

@ -23,16 +23,6 @@ using namespace winrt::Windows::Graphics::Display;
using namespace winrt::Windows::System;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
// The minimum delay between updates to the scroll bar's values.
// The updates are throttled to limit power usage.
constexpr const auto ScrollBarUpdateInterval = std::chrono::milliseconds(8);
// The minimum delay between updating the TSF input control.
constexpr const auto TsfRedrawInterval = std::chrono::milliseconds(100);
// The minimum delay between updating the locations of regex patterns
constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds(500);
namespace winrt::Microsoft::Terminal::Control::implementation
{
// Helper static function to ensure that all ambiguous-width glyphs are reported as narrow.
@ -104,61 +94,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnTerminalTaskbarProgressChanged = std::bind(&ControlCore::_terminalTaskbarProgressChanged, this);
_terminal->TaskbarProgressChangedCallback(pfnTerminalTaskbarProgressChanged);
// Get our dispatcher. If we're hosted in-proc with XAML, this will get
// us the same dispatcher as TermControl::Dispatcher(). If we're out of
// proc, this'll return null. We'll need to instead make a new
// DispatcherQueue (on a new thread), so we can use that for throttled
// functions.
_dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
if (!_dispatcher)
{
auto controller{ winrt::Windows::System::DispatcherQueueController::CreateOnDedicatedThread() };
_dispatcher = controller.DispatcherQueue();
}
// A few different events should be throttled, so they don't fire absolutely all the time:
// * _tsfTryRedrawCanvas: When the cursor position moves, we need to
// inform TSF, so it can move the canvas for the composition. We
// throttle this so that we're not hopping across the process boundary
// every time that the cursor moves.
// * _updatePatternLocations: When there's new output, or we scroll the
// viewport, we should re-check if there are any visible hyperlinks.
// But we don't really need to do this every single time text is
// output, we can limit this update to once every 500ms.
// * _updateScrollBar: Same idea as the TSF update - we don't _really_
// need to hop across the process boundary every time text is output.
// We can throttle this to once every 8ms, which will get us out of
// the way of the main output & rendering threads.
_tsfTryRedrawCanvas = std::make_shared<ThrottledFuncTrailing<winrt::Windows::System::DispatcherQueue>>(
_dispatcher,
TsfRedrawInterval,
[weakThis = get_weak()]() {
if (auto core{ weakThis.get() })
{
core->_CursorPositionChangedHandlers(*core, nullptr);
}
});
_updatePatternLocations = std::make_shared<ThrottledFuncTrailing<winrt::Windows::System::DispatcherQueue>>(
_dispatcher,
UpdatePatternLocationsInterval,
[weakThis = get_weak()]() {
if (auto core{ weakThis.get() })
{
core->UpdatePatternLocations();
}
});
_updateScrollBar = std::make_shared<ThrottledFuncTrailing<winrt::Windows::System::DispatcherQueue, Control::ScrollPositionChangedArgs>>(
_dispatcher,
ScrollBarUpdateInterval,
[weakThis = get_weak()](const auto& update) {
if (auto core{ weakThis.get() })
{
core->_ScrollPositionChangedHandlers(*core, update);
}
});
UpdateSettings(settings);
}
@ -1141,21 +1076,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// TODO GH#9617: refine locking around pattern tree
_terminal->ClearPatternTree();
// Start the throttled update of our scrollbar.
auto update{ winrt::make<ScrollPositionChangedArgs>(viewTop,
viewHeight,
bufferSize) };
_updateScrollBar->Run(update);
// Additionally, start the throttled update of where our links are.
_updatePatternLocations->Run();
_ScrollPositionChangedHandlers(*this,
winrt::make<ScrollPositionChangedArgs>(viewTop,
viewHeight,
bufferSize));
}
void ControlCore::_terminalCursorPositionChanged()
{
// When the buffer's cursor moves, start the throttled func to
// eventually dispatch a CursorPositionChanged event.
_tsfTryRedrawCanvas->Run();
_CursorPositionChangedHandlers(*this, nullptr);
}
void ControlCore::_terminalTaskbarProgressChanged()
@ -1443,8 +1372,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_terminal->Write(hstr);
// Start the throttled update of where our hyperlinks are.
_updatePatternLocations->Run();
// NOTE: We're raising an event here to inform the TermControl that
// output has been received, so it can queue up a throttled
// UpdatePatternLocations call. In the future, we should have the
// _updatePatternLocations ThrottledFunc internal to this class, and
// run on this object's dispatcher queue.
//
// We're not doing that quite yet, because the Core will eventually
// be out-of-proc from the UI thread, and won't be able to just use
// the UI thread as the dispatcher queue thread.
//
// See TODO: https://github.com/microsoft/terminal/projects/5#card-50760282
_ReceivedOutputHandlers(*this, nullptr);
}
}

View file

@ -207,11 +207,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double _panelHeight{ 0 };
double _compositionScale{ 0 };
winrt::Windows::System::DispatcherQueue _dispatcher{ nullptr };
std::shared_ptr<ThrottledFuncTrailing<winrt::Windows::System::DispatcherQueue>> _tsfTryRedrawCanvas;
std::shared_ptr<ThrottledFuncTrailing<winrt::Windows::System::DispatcherQueue>> _updatePatternLocations;
std::shared_ptr<ThrottledFuncTrailing<winrt::Windows::System::DispatcherQueue, Control::ScrollPositionChangedArgs>> _updateScrollBar;
winrt::fire_and_forget _asyncCloseConnection();
void _setFontSize(int fontSize);

View file

@ -67,6 +67,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, connection);
_core = _interactivity.Core();
// Use a manual revoker on the output event, so we can immediately stop
// worrying about it on destruction.
_coreOutputEventToken = _core.ReceivedOutput({ this, &TermControl::_coreReceivedOutput });
// These events might all be triggered by the connection, but that
// should be drained and closed before we complete destruction. So these
// are safe.
@ -108,7 +112,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Core eventually won't have access to. When we get to
// https://github.com/microsoft/terminal/projects/5#card-50760282
// then we'll move the applicable ones.
_tsfTryRedrawCanvas = std::make_shared<ThrottledFuncTrailing<winrt::Windows::UI::Core::CoreDispatcher>>(
_tsfTryRedrawCanvas = std::make_shared<ThrottledFuncTrailing<>>(
Dispatcher(),
TsfRedrawInterval,
[weakThis = get_weak()]() {
@ -118,7 +122,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
});
_playWarningBell = std::make_shared<ThrottledFuncLeading<winrt::Windows::UI::Core::CoreDispatcher>>(
_updatePatternLocations = std::make_shared<ThrottledFuncTrailing<>>(
Dispatcher(),
UpdatePatternLocationsInterval,
[weakThis = get_weak()]() {
if (auto control{ weakThis.get() })
{
control->_core.UpdatePatternLocations();
}
});
_playWarningBell = std::make_shared<ThrottledFuncLeading>(
Dispatcher(),
TerminalWarningBellInterval,
[weakThis = get_weak()]() {
@ -128,7 +142,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
});
_updateScrollBar = std::make_shared<ThrottledFuncTrailing<winrt::Windows::UI::Core::CoreDispatcher, ScrollBarUpdate>>(
_updateScrollBar = std::make_shared<ThrottledFuncTrailing<ScrollBarUpdate>>(
Dispatcher(),
ScrollBarUpdateInterval,
[weakThis = get_weak()](const auto& update) {
@ -530,7 +544,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// create a custom automation peer with this code pattern:
// (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers)
if (const auto& interactivityAutoPeer{ _interactivity.OnCreateAutomationPeer() })
if (const auto& interactivityAutoPeer = _interactivity.OnCreateAutomationPeer())
{
auto autoPeer = winrt::make_self<implementation::TermControlAutomationPeer>(this, interactivityAutoPeer);
_automationPeer = *autoPeer;
@ -1249,6 +1263,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
CATCH_LOG();
}
void TermControl::_coreReceivedOutput(const IInspectable& /*sender*/,
const IInspectable& /*args*/)
{
// Queue up a throttled UpdatePatternLocations call. In the future, we
// should have the _updatePatternLocations ThrottledFunc internal to
// ControlCore, and run on that object's dispatcher queue.
//
// We're not doing that quite yet, because the Core will eventually
// be out-of-proc from the UI thread, and won't be able to just use
// the UI thread as the dispatcher queue thread.
//
// THIS IS CALLED ON EVERY STRING OF TEXT OUTPUT TO THE TERMINAL. Think
// twice before adding anything here.
_updatePatternLocations->Run();
}
// Method Description:
// - Reset the font size of the terminal to its default size.
// Arguments:
@ -1286,6 +1317,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_updateScrollBar->ModifyPending([](auto& update) {
update.newValue.reset();
});
_updatePatternLocations->Run();
}
// Method Description:
@ -1623,6 +1656,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
update.newValue = args.ViewTop();
_updateScrollBar->Run(update);
_updatePatternLocations->Run();
}
// Method Description:
@ -1697,6 +1731,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// has run, we should disconnect them *right now*. If we don't, they may fire between the
// throttle delay (from the final output) and the dtor.
_tsfTryRedrawCanvas.reset();
_updatePatternLocations.reset();
_updateScrollBar.reset();
_playWarningBell.reset();

View file

@ -28,8 +28,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection);
static Control::TermControl FromConnectionInfo(IControlSettings settings, TerminalConnection::ConnectionInformation connectInfo);
winrt::fire_and_forget UpdateSettings();
winrt::fire_and_forget UpdateAppearance(const IControlAppearance newAppearance);
@ -156,8 +154,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _focused;
bool _initializedTerminal;
std::shared_ptr<ThrottledFuncTrailing<winrt::Windows::UI::Core::CoreDispatcher>> _tsfTryRedrawCanvas;
std::shared_ptr<ThrottledFuncLeading<winrt::Windows::UI::Core::CoreDispatcher>> _playWarningBell;
std::shared_ptr<ThrottledFuncTrailing<>> _tsfTryRedrawCanvas;
std::shared_ptr<ThrottledFuncTrailing<>> _updatePatternLocations;
std::shared_ptr<ThrottledFuncLeading> _playWarningBell;
struct ScrollBarUpdate
{
@ -166,9 +165,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double newMinimum;
double newViewportSize;
};
std::shared_ptr<ThrottledFuncTrailing<winrt::Windows::UI::Core::CoreDispatcher, ScrollBarUpdate>> _updateScrollBar;
std::shared_ptr<ThrottledFuncTrailing<ScrollBarUpdate>> _updateScrollBar;
bool _isInternalScrollBarUpdate;
// Auto scroll occurs when user, while selecting, drags cursor outside viewport. View is then scrolled to 'follow' the cursor.
@ -260,6 +257,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const int fontHeight,
const bool isInitialChange);
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
void _coreReceivedOutput(const IInspectable& sender, const IInspectable& args);
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
};

View file

@ -78,13 +78,13 @@ private:
// pending, then the previous call with the previous arguments will be
// cancelled and the call will be made with the new arguments instead.
// - The function will be run on the the specified dispatcher.
template<bool leading, typename T, typename... Args>
class ThrottledFunc : public std::enable_shared_from_this<ThrottledFunc<leading, T, Args...>>
template<bool leading, typename... Args>
class ThrottledFunc : public std::enable_shared_from_this<ThrottledFunc<leading, Args...>>
{
public:
using Func = std::function<void(Args...)>;
ThrottledFunc(T dispatcher, winrt::Windows::Foundation::TimeSpan delay, Func func) :
ThrottledFunc(winrt::Windows::UI::Core::CoreDispatcher dispatcher, winrt::Windows::Foundation::TimeSpan delay, Func func) :
_dispatcher{ std::move(dispatcher) },
_delay{ std::move(delay) },
_func{ std::move(func) }
@ -174,14 +174,13 @@ private:
}
}
T _dispatcher;
winrt::Windows::UI::Core::CoreDispatcher _dispatcher;
winrt::Windows::Foundation::TimeSpan _delay;
Func _func;
ThrottledFuncStorage<Args...> _storage;
};
template<typename T, typename... Args>
using ThrottledFuncTrailing = ThrottledFunc<false, T, Args...>;
template<typename T, typename... Args>
using ThrottledFuncLeading = ThrottledFunc<true, T, Args...>;
template<typename... Args>
using ThrottledFuncTrailing = ThrottledFunc<false, Args...>;
using ThrottledFuncLeading = ThrottledFunc<true>;