Dustin L. Howett 87c87b51fc Add support for regex search to conhost and Terminal (#17316)
This is broken down into individual reviewable commits.

[Here
is](https://github.com/microsoft/terminal/assets/189190/3b2ffd50-1350-4f3c-86b0-75abbd846969)
a video of it in action!

Part of #3920

(cherry picked from commit ecb56314762dcb9b41e177d05d669b28bbbe1490)
Service-Card-Id: PVTI_lADOAF3p4s4AmhmszgTTM0g
Service-Version: 1.21
2024-09-24 14:36:23 -05:00

469 lines
24 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "SearchBoxControl.h"
#include "TermControl.g.h"
#include "../../buffer/out/search.h"
#include "../../cascadia/TerminalCore/Terminal.hpp"
#include "../../renderer/uia/UiaRenderer.hpp"
#include "../../tsf/Handle.h"
#include "ControlInteractivity.h"
#include "ControlSettings.h"
namespace Microsoft::Console::VirtualTerminal
{
struct MouseButtonState;
}
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct TermControl;
struct TsfDataProvider : ::Microsoft::Console::TSF::IDataProvider
{
explicit TsfDataProvider(TermControl* termControl) noexcept;
virtual ~TsfDataProvider() = default;
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj) noexcept override;
ULONG STDMETHODCALLTYPE AddRef() noexcept override;
ULONG STDMETHODCALLTYPE Release() noexcept override;
HWND GetHwnd() override;
RECT GetViewport() override;
RECT GetCursorPosition() override;
void HandleOutput(std::wstring_view text) override;
::Microsoft::Console::Render::Renderer* GetRenderer() override;
private:
ControlCore* _getCore() const noexcept;
TermControl* _termControl = nullptr;
HWND _hwnd = nullptr;
};
struct TermControl : TermControlT<TermControl>
{
TermControl(Control::ControlInteractivity content);
TermControl(IControlSettings settings, Control::IControlAppearance unfocusedAppearance, TerminalConnection::ITerminalConnection connection);
static Control::TermControl NewControlByAttachingContent(Control::ControlInteractivity content, const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings);
winrt::fire_and_forget UpdateControlSettings(Control::IControlSettings settings, Control::IControlAppearance unfocusedAppearance);
IControlSettings Settings() const;
uint64_t ContentId() const;
hstring GetProfileName() const;
bool CopySelectionToClipboard(bool dismissSelection, bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
void PasteTextFromClipboard();
void SelectAll();
bool ToggleBlockSelection();
void ToggleMarkMode();
bool SwitchSelectionEndpoint();
bool ExpandSelectionToWord();
void RestoreFromPath(winrt::hstring path);
void PersistToPath(const winrt::hstring& path) const;
void Close();
Windows::Foundation::Size CharacterDimensions() const;
Windows::Foundation::Size MinimumSize();
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
Windows::Foundation::Point CursorPositionInDips();
void WindowVisibilityChanged(const bool showOrHide);
void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode);
#pragma region ICoreState
const uint64_t TaskbarState() const noexcept;
const uint64_t TaskbarProgress() const noexcept;
hstring Title();
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() noexcept;
hstring WorkingDirectory() const;
TerminalConnection::ConnectionState ConnectionState() const;
int ScrollOffset() const;
int ViewHeight() const;
int BufferHeight() const;
bool HasSelection() const;
bool HasMultiLineSelection() const;
winrt::hstring SelectedText(bool trimTrailingWhitespace) const;
bool BracketedPasteEnabled() const noexcept;
float BackgroundOpacity() const;
uint64_t OwningHwnd();
void OwningHwnd(uint64_t owner);
Windows::Foundation::Collections::IVector<Control::ScrollMark> ScrollMarks() const;
void AddMark(const Control::ScrollMark& mark);
void ClearMark();
void ClearAllMarks();
void ScrollToMark(const Control::ScrollToMarkDirection& direction);
void SelectCommand(const bool goUp);
void SelectOutput(const bool goUp);
#pragma endregion
void ScrollViewport(int viewTop);
void AdjustFontSize(float fontSizeDelta);
void ResetFontSize();
til::point GetFontSize() const;
void SendInput(const winrt::hstring& input);
void ClearBuffer(Control::ClearBufferType clearType);
void ToggleShaderEffects();
void RenderEngineSwapChainChanged(IInspectable sender, IInspectable args);
void _AttachDxgiSwapChainToXaml(HANDLE swapChainHandle);
winrt::fire_and_forget _RendererEnteredErrorState(IInspectable sender, IInspectable args);
void _RenderRetryButton_Click(const IInspectable& button, const IInspectable& args);
winrt::fire_and_forget _RendererWarning(IInspectable sender,
Control::RendererWarningArgs args);
void CreateSearchBoxControl();
void SearchMatch(const bool goForward);
bool SearchBoxEditInFocus() const;
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
~TermControl();
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
const Windows::UI::Xaml::Thickness GetPadding();
static Windows::Foundation::Size GetProposedDimensions(const IControlSettings& settings,
const uint32_t dpi,
int32_t commandlineCols,
int32_t commandlineRows);
static Windows::Foundation::Size GetProposedDimensions(const IControlSettings& settings, const uint32_t dpi, const winrt::Windows::Foundation::Size& initialSizeInChars);
void BellLightOn();
bool ReadOnly() const noexcept;
void ToggleReadOnly();
void SetReadOnly(const bool readOnlyState);
static Control::MouseButtonState GetPressedMouseButtons(const winrt::Windows::UI::Input::PointerPoint point);
static unsigned int GetPointerUpdateKind(const winrt::Windows::UI::Input::PointerPoint point);
static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
hstring ReadEntireBuffer() const;
Control::CommandHistoryContext CommandHistory() const;
winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept;
void AdjustOpacity(const float opacity, const bool relative);
bool RawWriteKeyEvent(const WORD vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
bool RawWriteChar(const wchar_t character, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers);
void RawWriteString(const winrt::hstring& text);
void ShowContextMenu();
void Detach();
TerminalConnection::ITerminalConnection Connection();
void Connection(const TerminalConnection::ITerminalConnection& connection);
Control::CursorDisplayState CursorVisibility() const noexcept;
void CursorVisibility(Control::CursorDisplayState cursorVisibility);
// -------------------------------- WinRT Events ---------------------------------
// clang-format off
til::property_changed_event PropertyChanged;
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
til::typed_event<IInspectable, Control::NoticeEventArgs> RaiseNotice;
til::typed_event<> HidePointerCursor;
til::typed_event<> RestorePointerCursor;
til::typed_event<> ReadOnlyChanged;
til::typed_event<IInspectable, IInspectable> FocusFollowMouseRequested;
til::typed_event<Control::TermControl, Windows::UI::Xaml::RoutedEventArgs> Initialized;
til::typed_event<> WarningBell;
til::typed_event<IInspectable, Control::KeySentEventArgs> KeySent;
til::typed_event<IInspectable, Control::CharSentEventArgs> CharSent;
til::typed_event<IInspectable, Control::StringSentEventArgs> StringSent;
// UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE
// Those attach the handler to the core directly, and will explode if
// the core ever gets detached & reattached to another window.
BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs);
BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable);
BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
// clang-format on
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, PropertyChanged.raise, nullptr);
private:
friend struct TermControlT<TermControl>; // friend our parent so it can bind private event handlers
friend struct TsfDataProvider;
// NOTE: _uiaEngine must be ordered before _core.
//
// ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer, which we own.
// We must ensure that we first destroy the ControlCore before the UiaEngine instance
// in order to safely resolve this unsafe pointer dependency. Otherwise a deallocated
// IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown.
// (C++ class members are destroyed in reverse order.)
// Further, the TermControlAutomationPeer must be destructed after _uiaEngine!
Control::TermControlAutomationPeer _automationPeer{ nullptr };
Control::ControlInteractivity _interactivity{ nullptr };
Control::ControlCore _core{ nullptr };
TsfDataProvider _tsfDataProvider{ this };
winrt::com_ptr<SearchBoxControl> _searchBox;
enum class AltNumpadEncoding
{
OEM,
ANSI,
Unicode,
};
struct AltNumpadState
{
struct CachedKey
{
WORD vkey;
WORD scanCode;
::Microsoft::Terminal::Core::ControlKeyStates modifiers;
bool keyDown;
};
AltNumpadEncoding encoding = AltNumpadEncoding::OEM;
uint32_t accumulator = 0;
// Checking for accumulator != 0 to see if we have an ongoing Alt+Numpad composition is insufficient.
// The state can be active, while the accumulator is 0, if the user pressed Alt+Numpad0 which enabled
// the OEM encoding mode (= active), and then pressed Numpad0 again (= accumulator is still 0).
bool active = false;
til::small_vector<CachedKey, 4> cachedKeyEvents;
};
AltNumpadState _altNumpadState;
bool _closing{ false };
bool _focused{ false };
bool _initializedTerminal{ false };
std::shared_ptr<ThrottledFuncLeading> _playWarningBell;
struct ScrollBarUpdate
{
std::optional<double> newValue;
double newMaximum;
double newMinimum;
double newViewportSize;
};
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.
double _autoScrollVelocity;
std::optional<Windows::UI::Input::PointerPoint> _autoScrollingPointerPoint;
SafeDispatcherTimer _autoScrollTimer;
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
bool _pointerPressedInBounds{ false };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation{ nullptr };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellDarkAnimation{ nullptr };
SafeDispatcherTimer _bellLightTimer;
SafeDispatcherTimer _cursorTimer;
SafeDispatcherTimer _blinkTimer;
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
winrt::hstring _restorePath;
bool _showMarksInScrollbar{ false };
bool _isBackgroundLight{ false };
bool _detached{ false };
Windows::Foundation::Collections::IObservableVector<Windows::UI::Xaml::Controls::ICommandBarElement> _originalPrimaryElements{ nullptr };
Windows::Foundation::Collections::IObservableVector<Windows::UI::Xaml::Controls::ICommandBarElement> _originalSecondaryElements{ nullptr };
Windows::Foundation::Collections::IObservableVector<Windows::UI::Xaml::Controls::ICommandBarElement> _originalSelectedPrimaryElements{ nullptr };
Windows::Foundation::Collections::IObservableVector<Windows::UI::Xaml::Controls::ICommandBarElement> _originalSelectedSecondaryElements{ nullptr };
Control::CursorDisplayState _cursorVisibility{ Control::CursorDisplayState::Default };
inline bool _IsClosing() const noexcept
{
#ifndef NDEBUG
// _closing isn't atomic and may only be accessed from the main thread.
if (const auto dispatcher = Dispatcher())
{
assert(dispatcher.HasThreadAccess());
}
#endif
return _closing;
}
void _initializeForAttach(const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
void _UpdateSettingsFromUIThread();
void _UpdateAppearanceFromUIThread(Control::IControlAppearance newAppearance);
void _ApplyUISettings();
winrt::fire_and_forget UpdateAppearance(Control::IControlAppearance newAppearance);
void _SetBackgroundImage(const IControlAppearance& newAppearance);
void _InitializeBackgroundBrush();
winrt::fire_and_forget _coreBackgroundColorChanged(const IInspectable& sender, const IInspectable& args);
void _changeBackgroundColor(til::color bg);
static bool _isColorLight(til::color bg) noexcept;
void _changeBackgroundOpacity();
enum InitializeReason : bool
{
Create,
Reattach
};
bool _InitializeTerminal(const InitializeReason reason);
winrt::fire_and_forget _restoreInBackground();
void _SetFontSize(int fontSize);
void _TappedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::TappedRoutedEventArgs& e);
void _KeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _KeyUpHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _CharacterHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs& e);
void _PointerPressedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _PointerMovedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _PointerReleasedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _PointerExitedHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _MouseWheelHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _ScrollbarChangeHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs& e);
void _GotFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
void _LostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
winrt::fire_and_forget _DragDropHandler(Windows::Foundation::IInspectable sender, Windows::UI::Xaml::DragEventArgs e);
void _DragOverHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::DragEventArgs& e);
winrt::fire_and_forget _HyperlinkHandler(Windows::Foundation::IInspectable sender, Control::OpenHyperlinkEventArgs e);
void _CursorTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _BlinkTimerTick(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _BellLightOff(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _SetEndSelectionPointAtCursor(const Windows::Foundation::Point& cursorPosition);
void _SwapChainSizeChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::SizeChangedEventArgs& e);
void _SwapChainScaleChanged(const Windows::UI::Xaml::Controls::SwapChainPanel& sender, const Windows::Foundation::IInspectable& args);
void _TerminalTabColorChanged(const std::optional<til::color> color);
void _ScrollPositionChanged(const IInspectable& sender, const Control::ScrollPositionChangedArgs& args);
bool _CapturePointer(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
bool _ReleasePointerCapture(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _TryStartAutoScroll(const Windows::UI::Input::PointerPoint& pointerPoint, const double scrollVelocity);
void _TryStopAutoScroll(const uint32_t pointerId);
void _UpdateAutoScroll(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _KeyHandler(const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e, const bool keyDown);
bool _KeyHandler(WORD vkey, WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers, bool keyDown);
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;
bool _TryHandleKeyBinding(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers) const;
static void _ClearKeyboardState(const WORD vkey, const WORD scanCode) noexcept;
bool _TrySendKeyEvent(const WORD vkey, const WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
til::point _toControlOrigin(const til::point terminalPosition);
const til::point _toTerminalOrigin(winrt::Windows::Foundation::Point cursorPosition);
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive, const bool regularExpression);
void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive, const bool regularExpression);
void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void _refreshSearch();
void _handleSearchResults(SearchResults results);
void _hoveredHyperlinkChanged(const IInspectable& sender, const IInspectable& args);
winrt::fire_and_forget _updateSelectionMarkers(IInspectable sender, Control::UpdateSelectionMarkersEventArgs args);
void _coreFontSizeChanged(const IInspectable& s, const Control::FontSizeChangedArgs& args);
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
void _coreOutputIdle(const IInspectable& sender, const IInspectable& args);
til::point _toPosInDips(const Core::Point terminalCellPos);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
void _pasteTextWithBroadcast(const winrt::hstring& text);
void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args);
void _showContextMenuAt(const til::point& controlRelativePos);
void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args);
void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args);
void _SearchCommandHandler(const IInspectable& sender, const IInspectable& args);
void _SelectCommandHandler(const IInspectable& sender, const IInspectable& args);
void _SelectOutputHandler(const IInspectable& sender, const IInspectable& args);
bool _displayCursorWhileBlurred() const noexcept;
struct Revokers
{
Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged;
Control::ControlCore::WarningBell_revoker WarningBell;
Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState;
Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged;
Control::ControlCore::FontSizeChanged_revoker FontSizeChanged;
Control::ControlCore::TransparencyChanged_revoker TransparencyChanged;
Control::ControlCore::RaiseNotice_revoker RaiseNotice;
Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged;
Control::ControlCore::OutputIdle_revoker OutputIdle;
Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers;
Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink;
Control::ControlCore::TitleChanged_revoker TitleChanged;
Control::ControlCore::TabColorChanged_revoker TabColorChanged;
Control::ControlCore::TaskbarProgressChanged_revoker TaskbarProgressChanged;
Control::ControlCore::ConnectionStateChanged_revoker ConnectionStateChanged;
Control::ControlCore::ShowWindowChanged_revoker ShowWindowChanged;
Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested;
Control::ControlCore::CompletionsChanged_revoker CompletionsChanged;
Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested;
// These are set up in _InitializeTerminal
Control::ControlCore::RendererWarning_revoker RendererWarning;
Control::ControlCore::SwapChainChanged_revoker SwapChainChanged;
Control::ControlInteractivity::OpenHyperlink_revoker interactivityOpenHyperlink;
Control::ControlInteractivity::ScrollPositionChanged_revoker interactivityScrollPositionChanged;
Control::ControlInteractivity::PasteFromClipboard_revoker PasteFromClipboard;
Control::ControlInteractivity::ContextMenuRequested_revoker ContextMenuRequested;
} _revokers{};
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(TermControl);
}