Skip to content

Commit 06f736b

Browse files
authored
wpf: use the new TSF implementation (#18861)
This fixes two issues in the WPF terminal control: - The emoji picker and other IME candidate windows didn't show up in the right place - Submitting an emoji via the emoji picker would result in two win32 input mode events with a VK of 65535 and the surrogate pair halves. I am not sure I did the right thing with the thread TSF handle...
1 parent 865f5e5 commit 06f736b

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

src/cascadia/TerminalControl/HwndTerminal.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,79 @@ using namespace ::Microsoft::Terminal::Core;
1919

2020
static LPCWSTR term_window_class = L"HwndTerminalClass";
2121

22+
STDMETHODIMP HwndTerminal::TsfDataProvider::QueryInterface(REFIID, void**) noexcept
23+
{
24+
return E_NOTIMPL;
25+
}
26+
27+
ULONG STDMETHODCALLTYPE HwndTerminal::TsfDataProvider::AddRef() noexcept
28+
{
29+
return 1;
30+
}
31+
32+
ULONG STDMETHODCALLTYPE HwndTerminal::TsfDataProvider::Release() noexcept
33+
{
34+
return 1;
35+
}
36+
37+
HWND HwndTerminal::TsfDataProvider::GetHwnd()
38+
{
39+
return _terminal->GetHwnd();
40+
}
41+
42+
RECT HwndTerminal::TsfDataProvider::GetViewport()
43+
{
44+
const auto hwnd = GetHwnd();
45+
46+
RECT rc;
47+
GetClientRect(hwnd, &rc);
48+
49+
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclientrect
50+
// > The left and top members are zero. The right and bottom members contain the width and height of the window.
51+
// --> We can turn the client rect into a screen-relative rect by adding the left/top position.
52+
ClientToScreen(hwnd, reinterpret_cast<POINT*>(&rc));
53+
rc.right += rc.left;
54+
rc.bottom += rc.top;
55+
56+
return rc;
57+
}
58+
59+
RECT HwndTerminal::TsfDataProvider::GetCursorPosition()
60+
{
61+
// Convert from columns/rows to pixels.
62+
til::point cursorPos;
63+
til::size fontSize;
64+
{
65+
const auto lock = _terminal->_terminal->LockForReading();
66+
cursorPos = _terminal->_terminal->GetCursorPosition(); // measured in terminal cells
67+
fontSize = _terminal->_actualFont.GetSize(); // measured in pixels, not DIP
68+
}
69+
POINT ptSuggestion = {
70+
.x = cursorPos.x * fontSize.width,
71+
.y = cursorPos.y * fontSize.height,
72+
};
73+
74+
ClientToScreen(GetHwnd(), &ptSuggestion);
75+
76+
// Final measurement should be in pixels
77+
return {
78+
.left = ptSuggestion.x,
79+
.top = ptSuggestion.y,
80+
.right = ptSuggestion.x + fontSize.width,
81+
.bottom = ptSuggestion.y + fontSize.height,
82+
};
83+
}
84+
85+
void HwndTerminal::TsfDataProvider::HandleOutput(std::wstring_view text)
86+
{
87+
_terminal->_WriteTextToConnection(text);
88+
}
89+
90+
Microsoft::Console::Render::Renderer* HwndTerminal::TsfDataProvider::GetRenderer()
91+
{
92+
return _terminal->_renderer.get();
93+
}
94+
2295
// This magic flag is "documented" at https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
2396
// "If the high-order bit is 1, the key is down; otherwise, it is up."
2497
static constexpr short KeyPressed{ gsl::narrow_cast<short>(0x8000) };
@@ -242,6 +315,7 @@ try
242315
{
243316
// As a rule, detach resources from the Terminal before shutting them down.
244317
// This ensures that teardown is reentrant.
318+
_tsfHandle = {};
245319

246320
// Shut down the renderer (and therefore the thread) before we implode
247321
_renderer.reset();
@@ -941,6 +1015,16 @@ void __stdcall TerminalSetFocus(void* terminal)
9411015
{
9421016
LOG_IF_FAILED(uiaEngine->Enable());
9431017
}
1018+
publicTerminal->_FocusTSF();
1019+
}
1020+
1021+
void HwndTerminal::_FocusTSF() noexcept
1022+
{
1023+
if (!_tsfHandle)
1024+
{
1025+
_tsfHandle = Microsoft::Console::TSF::Handle::Create();
1026+
_tsfHandle.AssociateFocus(&_tsfDataProvider);
1027+
}
9441028
}
9451029

9461030
void __stdcall TerminalKillFocus(void* terminal)

src/cascadia/TerminalControl/HwndTerminal.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "../../buffer/out/textBuffer.hpp"
77
#include "../../renderer/inc/FontInfoDesired.hpp"
88
#include "../../types/IControlAccessibilityInfo.h"
9+
#include "../../tsf/Handle.h"
910

1011
namespace Microsoft::Console::Render::Atlas
1112
{
@@ -85,6 +86,21 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
8586
static LRESULT CALLBACK HwndTerminalWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;
8687

8788
private:
89+
struct TsfDataProvider : public Microsoft::Console::TSF::IDataProvider
90+
{
91+
TsfDataProvider(HwndTerminal* t) :
92+
_terminal(t) {}
93+
virtual ~TsfDataProvider() = default;
94+
STDMETHODIMP TsfDataProvider::QueryInterface(REFIID, void**) noexcept override;
95+
ULONG STDMETHODCALLTYPE TsfDataProvider::AddRef() noexcept override;
96+
ULONG STDMETHODCALLTYPE TsfDataProvider::Release() noexcept override;
97+
HWND GetHwnd() override;
98+
RECT GetViewport() override;
99+
RECT GetCursorPosition() override;
100+
void HandleOutput(std::wstring_view text) override;
101+
Microsoft::Console::Render::Renderer* GetRenderer() override;
102+
HwndTerminal* _terminal;
103+
};
88104
wil::unique_hwnd _hwnd;
89105
FontInfoDesired _desiredFont;
90106
FontInfo _actualFont;
@@ -106,6 +122,10 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
106122
std::optional<til::point> _lastMouseClickPos;
107123
std::optional<til::point> _singleClickTouchdownPos;
108124

125+
// _tsfHandle uses _tsfDataProvider. Destructors run from bottom to top; this maintains correct destruction order.
126+
TsfDataProvider _tsfDataProvider{ this };
127+
Microsoft::Console::TSF::Handle _tsfHandle;
128+
109129
friend HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ void** terminal);
110130
friend HRESULT _stdcall TerminalTriggerResize(_In_ void* terminal, _In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions);
111131
friend HRESULT _stdcall TerminalTriggerResizeWithDimension(_In_ void* terminal, _In_ til::size dimensions, _Out_ til::size* dimensionsInPixels);
@@ -129,6 +149,8 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
129149
HRESULT _CopyToSystemClipboard(const std::string& stringToCopy, LPCWSTR lpszFormat) const;
130150
void _PasteTextFromClipboard() noexcept;
131151

152+
void _FocusTSF() noexcept;
153+
132154
const unsigned int _NumberOfClicks(til::point clickPos, std::chrono::steady_clock::time_point clickTime) noexcept;
133155
HRESULT _StartSelection(LPARAM lParam) noexcept;
134156
HRESULT _MoveSelection(LPARAM lParam) noexcept;

0 commit comments

Comments
 (0)