Skip to content

Commit

Permalink
wpf: add support for VT mouse mode (#5375)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request

This pull request ports the VT mouse code from TermControl to WpfTerminalControl. Our WPF control is a lot closer to Win32 than to Xaml, so our mouse event handler looks _nothing_ like the one that we got from Xaml. We can pass events through almost directly, because the window message handling in the mouse input code actually came from _conhost_. It's awesome.

Neither TermControl nor conhost pass hover events through when the control isn't focused, so I wired up focus events to make sure we acted the same.

Just like Terminal and conhost, mouse events are suppressed when <kbd>Shift</kbd> is held.

## Validation Steps Performed
Tested with MC, and tested by manually engaging SGR events in an Echo terminal.

![image](https://user-images.githubusercontent.com/14316954/79417901-2f976a00-7f68-11ea-97e9-c053cbed3878.png)
  • Loading branch information
DHowett authored Apr 17, 2020
1 parent a6b2e7f commit f04b7aa
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 0 deletions.
58 changes: 58 additions & 0 deletions src/cascadia/PublicTerminalCore/HwndTerminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ using namespace ::Microsoft::Terminal::Core;

static LPCWSTR term_window_class = L"HwndTerminalClass";

static constexpr bool _IsMouseMessage(UINT uMsg)
{
return uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP || uMsg == WM_LBUTTONDBLCLK ||
uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP || uMsg == WM_MBUTTONDBLCLK ||
uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP || uMsg == WM_RBUTTONDBLCLK ||
uMsg == WM_MOUSEMOVE || uMsg == WM_MOUSEWHEEL;
}

LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(
HWND hwnd,
UINT uMsg,
Expand All @@ -27,6 +35,14 @@ LRESULT CALLBACK HwndTerminal::HwndTerminalWndProc(

if (terminal)
{
if (_IsMouseMessage(uMsg) && terminal->_CanSendVTMouseInput())
{
if (terminal->_SendMouseEvent(uMsg, wParam, lParam))
{
return 0;
}
}

switch (uMsg)
{
case WM_GETOBJECT:
Expand Down Expand Up @@ -509,6 +525,36 @@ static ControlKeyStates getControlKeyState() noexcept
return flags;
}

bool HwndTerminal::_CanSendVTMouseInput() const noexcept
{
// Only allow the transit of mouse events if shift isn't pressed.
const bool shiftPressed = GetKeyState(VK_SHIFT) < 0;
return !shiftPressed && _focused && _terminal->IsTrackingMouseInput();
}

bool HwndTerminal::_SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept
try
{
const til::point cursorPosition{
GET_X_LPARAM(lParam),
GET_Y_LPARAM(lParam),
};

const til::size fontSize{ this->_actualFont.GetSize() };
short wheelDelta{ 0 };
if (uMsg == WM_MOUSEWHEEL)
{
wheelDelta = HIWORD(wParam);
}

return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
}

void _stdcall TerminalSendKeyEvent(void* terminal, WORD vkey, WORD scanCode)
{
const auto publicTerminal = static_cast<const HwndTerminal*>(terminal);
Expand Down Expand Up @@ -596,6 +642,18 @@ void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible)
publicTerminal->_terminal->SetCursorOn(visible);
}

void __stdcall TerminalSetFocus(void* terminal)
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_focused = true;
}

void __stdcall TerminalKillFocus(void* terminal)
{
auto publicTerminal = static_cast<HwndTerminal*>(terminal);
publicTerminal->_focused = false;
}

// Routine Description:
// - Copies the text given onto the global system clipboard.
// Arguments:
Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/PublicTerminalCore/HwndTerminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ __declspec(dllexport) void _stdcall TerminalSendKeyEvent(void* terminal, WORD vk
__declspec(dllexport) void _stdcall TerminalSendCharEvent(void* terminal, wchar_t ch, WORD scanCode);
__declspec(dllexport) void _stdcall TerminalBlinkCursor(void* terminal);
__declspec(dllexport) void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
__declspec(dllexport) void _stdcall TerminalSetFocus(void* terminal);
__declspec(dllexport) void _stdcall TerminalKillFocus(void* terminal);
};

struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
Expand Down Expand Up @@ -75,6 +77,8 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
std::unique_ptr<::Microsoft::Console::Render::Renderer> _renderer;
std::unique_ptr<::Microsoft::Console::Render::DxEngine> _renderEngine;

bool _focused{ false };

std::chrono::milliseconds _multiClickTime;
unsigned int _multiClickCounter{};
std::chrono::steady_clock::time_point _lastMouseClickTimestamp{};
Expand All @@ -93,6 +97,8 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
friend void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR fontFamily, short fontSize, int newDpi);
friend void _stdcall TerminalBlinkCursor(void* terminal);
friend void _stdcall TerminalSetCursorVisible(void* terminal, const bool visible);
friend void _stdcall TerminalSetFocus(void* terminal);
friend void _stdcall TerminalKillFocus(void* terminal);

void _UpdateFont(int newDpi);
void _WriteTextToConnection(const std::wstring& text) noexcept;
Expand All @@ -106,6 +112,9 @@ struct HwndTerminal : ::Microsoft::Console::Types::IControlAccessibilityInfo
HRESULT _MoveSelection(LPARAM lParam) noexcept;
IRawElementProviderSimple* _GetUiaProvider() noexcept;

bool _CanSendVTMouseInput() const noexcept;
bool _SendMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) noexcept;

// Inherited via IControlAccessibilityInfo
COORD GetFontSize() const override;
RECT GetBounds() const noexcept override;
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/WpfTerminalControl/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ public enum SetWindowPosFlags : uint
[DllImport("PublicTerminalCore.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void TerminalSetCursorVisible(IntPtr terminal, bool visible);

[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern void TerminalSetFocus(IntPtr terminal);

[DllImport("PublicTerminalCore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern void TerminalKillFocus(IntPtr terminal);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetFocus(IntPtr hWnd);

Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/WpfTerminalControl/TerminalContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,11 @@ private IntPtr TerminalContainer_MessageHook(IntPtr hwnd, int msg, IntPtr wParam
switch ((NativeMethods.WindowMessage)msg)
{
case NativeMethods.WindowMessage.WM_SETFOCUS:
NativeMethods.TerminalSetFocus(this.terminal);
this.blinkTimer?.Start();
break;
case NativeMethods.WindowMessage.WM_KILLFOCUS:
NativeMethods.TerminalKillFocus(this.terminal);
this.blinkTimer?.Stop();
NativeMethods.TerminalSetCursorVisible(this.terminal, false);
break;
Expand Down

0 comments on commit f04b7aa

Please sign in to comment.