diff --git a/src/cascadia/TerminalControl/HwndTerminal.cpp b/src/cascadia/TerminalControl/HwndTerminal.cpp index d2aa1e1796..3cb02ee547 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.cpp +++ b/src/cascadia/TerminalControl/HwndTerminal.cpp @@ -19,6 +19,79 @@ using namespace ::Microsoft::Terminal::Core; static LPCWSTR term_window_class = L"HwndTerminalClass"; +STDMETHODIMP HwndTerminal::TsfDataProvider::QueryInterface(REFIID, void**) noexcept +{ + return E_NOTIMPL; +} + +ULONG STDMETHODCALLTYPE HwndTerminal::TsfDataProvider::AddRef() noexcept +{ + return 1; +} + +ULONG STDMETHODCALLTYPE HwndTerminal::TsfDataProvider::Release() noexcept +{ + return 1; +} + +HWND HwndTerminal::TsfDataProvider::GetHwnd() +{ + return _terminal->GetHwnd(); +} + +RECT HwndTerminal::TsfDataProvider::GetViewport() +{ + const auto hwnd = GetHwnd(); + + RECT rc; + GetClientRect(hwnd, &rc); + + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect + // > The left and top members are zero. The right and bottom members contain the width and height of the window. + // --> We can turn the client rect into a screen-relative rect by adding the left/top position. + ClientToScreen(hwnd, reinterpret_cast(&rc)); + rc.right += rc.left; + rc.bottom += rc.top; + + return rc; +} + +RECT HwndTerminal::TsfDataProvider::GetCursorPosition() +{ + // Convert from columns/rows to pixels. + til::point cursorPos; + til::size fontSize; + { + const auto lock = _terminal->_terminal->LockForReading(); + cursorPos = _terminal->_terminal->GetCursorPosition(); // measured in terminal cells + fontSize = _terminal->_actualFont.GetSize(); // measured in pixels, not DIP + } + POINT ptSuggestion = { + .x = cursorPos.x * fontSize.width, + .y = cursorPos.y * fontSize.height, + }; + + ClientToScreen(GetHwnd(), &ptSuggestion); + + // Final measurement should be in pixels + return { + .left = ptSuggestion.x, + .top = ptSuggestion.y, + .right = ptSuggestion.x + fontSize.width, + .bottom = ptSuggestion.y + fontSize.height, + }; +} + +void HwndTerminal::TsfDataProvider::HandleOutput(std::wstring_view text) +{ + _terminal->_WriteTextToConnection(text); +} + +Microsoft::Console::Render::Renderer* HwndTerminal::TsfDataProvider::GetRenderer() +{ + return _terminal->_renderer.get(); +} + // This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx // "If the high-order bit is 1, the key is down; otherwise, it is up." static constexpr short KeyPressed{ gsl::narrow_cast(0x8000) }; @@ -242,6 +315,7 @@ try { // As a rule, detach resources from the Terminal before shutting them down. // This ensures that teardown is reentrant. + _tsfHandle = {}; // Shut down the renderer (and therefore the thread) before we implode _renderer.reset(); @@ -941,6 +1015,16 @@ void __stdcall TerminalSetFocus(void* terminal) { LOG_IF_FAILED(uiaEngine->Enable()); } + publicTerminal->_FocusTSF(); +} + +void HwndTerminal::_FocusTSF() noexcept +{ + if (!_tsfHandle) + { + _tsfHandle = Microsoft::Console::TSF::Handle::Create(); + _tsfHandle.AssociateFocus(&_tsfDataProvider); + } } void __stdcall TerminalKillFocus(void* terminal) diff --git a/src/cascadia/TerminalControl/HwndTerminal.hpp b/src/cascadia/TerminalControl/HwndTerminal.hpp index 8ecd32df02..350727f12a 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.hpp +++ b/src/cascadia/TerminalControl/HwndTerminal.hpp @@ -6,6 +6,7 @@ #include "../../buffer/out/textBuffer.hpp" #include "../../renderer/inc/FontInfoDesired.hpp" #include "../../types/IControlAccessibilityInfo.h" +#include "../../tsf/Handle.h" namespace Microsoft::Console::Render::Atlas { @@ -85,6 +86,21 @@ public: static LRESULT CALLBACK HwndTerminalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept; private: + struct TsfDataProvider : public Microsoft::Console::TSF::IDataProvider + { + TsfDataProvider(HwndTerminal* t) : + _terminal(t) {} + virtual ~TsfDataProvider() = default; + STDMETHODIMP TsfDataProvider::QueryInterface(REFIID, void**) noexcept override; + ULONG STDMETHODCALLTYPE TsfDataProvider::AddRef() noexcept override; + ULONG STDMETHODCALLTYPE TsfDataProvider::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; + HwndTerminal* _terminal; + }; wil::unique_hwnd _hwnd; FontInfoDesired _desiredFont; FontInfo _actualFont; @@ -106,6 +122,10 @@ private: std::optional _lastMouseClickPos; std::optional _singleClickTouchdownPos; + // _tsfHandle uses _tsfDataProvider. Destructors run from bottom to top; this maintains correct destruction order. + TsfDataProvider _tsfDataProvider{ this }; + Microsoft::Console::TSF::Handle _tsfHandle; + friend HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal); friend HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions); friend HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ til::size dimensions, _Out_ til::size* dimensionsInPixels); @@ -129,6 +149,8 @@ private: HRESULT _CopyToSystemClipboard(const std::string& stringToCopy, LPCWSTR lpszFormat) const; void _PasteTextFromClipboard() noexcept; + void _FocusTSF() noexcept; + const unsigned int _NumberOfClicks(til::point clickPos, std::chrono::steady_clock::time_point clickTime) noexcept; HRESULT _StartSelection(LPARAM lParam) noexcept; HRESULT _MoveSelection(LPARAM lParam) noexcept;