Add support for VT horizontal mouse wheel events (#19248)

This adds support for horizontal mouse wheel events (`WM_MOUSEHWHEEL`).
With this change, applications running in the terminal can now receive
and respond to horizontal scroll inputs from the mouse/trackpad.

Closes #19245
Closes #10329
This commit is contained in:
Ayman Bagabas 2025-09-11 19:48:49 -04:00 committed by GitHub
parent eb16eb26ab
commit 814f78ed2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 134 additions and 67 deletions

View File

@ -485,7 +485,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - modifiers: The modifiers pressed during this event, in the form of a VirtualKeyModifiers
// - delta: the mouse wheel delta that triggered this event.
bool ControlInteractivity::MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const int32_t delta,
const Core::Point delta,
const Core::Point pixelPosition,
const Control::MouseButtonState buttonState)
{
@ -506,9 +506,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// PointerPoint to work with. So, we're just going to do a
// mousewheel event manually
return _sendMouseEventHelper(terminalPosition,
WM_MOUSEWHEEL,
delta.Y != 0 ? WM_MOUSEWHEEL : WM_MOUSEHWHEEL,
modifiers,
::base::saturated_cast<short>(delta),
::base::saturated_cast<short>(delta.Y != 0 ? delta.Y : delta.X),
buttonState);
}
@ -517,15 +517,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (ctrlPressed && shiftPressed && _core->Settings().ScrollToChangeOpacity())
{
_mouseTransparencyHandler(delta);
_mouseTransparencyHandler(delta.Y);
}
else if (ctrlPressed && !shiftPressed && _core->Settings().ScrollToZoom())
{
_mouseZoomHandler(delta);
_mouseZoomHandler(delta.Y);
}
else
{
_mouseScrollHandler(delta, pixelPosition, WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown));
_mouseScrollHandler(delta.Y, pixelPosition, WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown));
}
return false;
}
@ -659,7 +659,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core->IsVtMouseModeEnabled();
}
bool ControlInteractivity::_shouldSendAlternateScroll(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta)
bool ControlInteractivity::_shouldSendAlternateScroll(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const Core::Point delta)
{
// If the user is holding down Shift, suppress mouse events
// TODO GH#4875: disable/customize this functionality
@ -667,7 +667,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
return false;
}
return _core->ShouldSendAlternateScroll(WM_MOUSEWHEEL, delta);
if (delta.Y != 0)
{
return _core->ShouldSendAlternateScroll(WM_MOUSEWHEEL, delta.Y);
}
else
{
return _core->ShouldSendAlternateScroll(WM_MOUSEHWHEEL, delta.X);
}
}
// Method Description:

View File

@ -74,7 +74,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TouchReleased();
bool MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const int32_t delta,
const Core::Point delta,
const Core::Point pixelPosition,
const Control::MouseButtonState state);
@ -153,7 +153,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _hyperlinkHandler(const std::wstring_view uri);
bool _canSendVTMouseInput(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers);
bool _shouldSendAlternateScroll(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta);
bool _shouldSendAlternateScroll(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const Core::Point delta);
til::point _getTerminalPosition(const til::point pixelPosition, bool roundToNearestCell);

View File

@ -60,7 +60,7 @@ namespace Microsoft.Terminal.Control
void TouchReleased();
Boolean MouseWheel(Microsoft.Terminal.Core.ControlKeyStates modifiers,
Int32 delta,
Microsoft.Terminal.Core.Point delta,
Microsoft.Terminal.Core.Point pixelPosition,
MouseButtonState state);

View File

@ -11,6 +11,6 @@ namespace Microsoft.Terminal.Control
[uuid("65b8b8c5-988f-43ff-aba9-e89368da1598")]
interface IMouseWheelListener
{
Boolean OnMouseWheel(Windows.Foundation.Point coord, Int32 delta, Boolean leftButtonDown, Boolean midButtonDown, Boolean rightButtonDown);
Boolean OnMouseWheel(Windows.Foundation.Point coord, Microsoft.Terminal.Core.Point delta, Boolean leftButtonDown, Boolean midButtonDown, Boolean rightButtonDown);
}
}

View File

@ -2160,15 +2160,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
RestorePointerCursor.raise(*this, nullptr);
const auto point = args.GetCurrentPoint(*this);
// GH#10329 - we don't need to handle horizontal scrolls. Only vertical ones.
// So filter out the horizontal ones.
if (point.Properties().IsHorizontalMouseWheel())
{
return;
}
auto delta = point.Properties().MouseWheelDelta();
auto result = _interactivity.MouseWheel(ControlKeyStates{ args.KeyModifiers() },
point.Properties().MouseWheelDelta(),
point.Properties().IsHorizontalMouseWheel() ?
Core::Point{ delta, 0 } :
Core::Point{ 0, delta },
_toTerminalOrigin(point.Position()),
TermControl::GetPressedMouseButtons(point));
if (result)
@ -2188,7 +2184,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - delta: the mouse wheel delta that triggered this event.
// - state: the state for each of the mouse buttons individually (pressed/unpressed)
bool TermControl::OnMouseWheel(const Windows::Foundation::Point location,
const int32_t delta,
const Core::Point delta,
const bool leftButtonDown,
const bool midButtonDown,
const bool rightButtonDown)

View File

@ -148,7 +148,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
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);
bool OnMouseWheel(const Windows::Foundation::Point location, const winrt::Microsoft::Terminal::Core::Point delta, const bool leftButtonDown, const bool midButtonDown, const bool rightButtonDown);
~TermControl();

View File

@ -170,7 +170,7 @@ namespace ControlUnitTests
// The mouse location and buttons don't matter here.
interactivity->MouseWheel(modifiers,
30,
Core::Point{ 0, 30 },
Core::Point{ 0, 0 },
buttonState);
}
@ -188,7 +188,7 @@ namespace ControlUnitTests
// The mouse location and buttons don't matter here.
interactivity->MouseWheel(modifiers,
-30,
Core::Point{ 0, -30 },
Core::Point{ 0, 0 },
buttonState);
}
@ -245,7 +245,7 @@ namespace ControlUnitTests
expectedTop = 20;
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
Core::Point{ 0, WHEEL_DELTA },
Core::Point{ 0, 0 },
buttonState);
@ -254,18 +254,18 @@ namespace ControlUnitTests
{
expectedTop--;
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
Core::Point{ 0, WHEEL_DELTA },
Core::Point{ 0, 0 },
buttonState);
}
Log::Comment(L"Scrolling up more should do nothing");
expectedTop = 0;
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
Core::Point{ 0, WHEEL_DELTA },
Core::Point{ 0, 0 },
buttonState);
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
Core::Point{ 0, WHEEL_DELTA },
Core::Point{ 0, 0 },
buttonState);
@ -275,7 +275,7 @@ namespace ControlUnitTests
Log::Comment(NoThrowString().Format(L"---scroll down #%d---", i));
expectedTop++;
interactivity->MouseWheel(modifiers,
-WHEEL_DELTA,
Core::Point{ 0, -WHEEL_DELTA },
Core::Point{ 0, 0 },
buttonState);
Log::Comment(NoThrowString().Format(L"internal scrollbar pos:%f", interactivity->_internalScrollbarPosition));
@ -283,11 +283,11 @@ namespace ControlUnitTests
Log::Comment(L"Scrolling down more should do nothing");
expectedTop = 21;
interactivity->MouseWheel(modifiers,
-WHEEL_DELTA,
Core::Point{ 0, -WHEEL_DELTA },
Core::Point{ 0, 0 },
buttonState);
interactivity->MouseWheel(modifiers,
-WHEEL_DELTA,
Core::Point{ 0, -WHEEL_DELTA },
Core::Point{ 0, 0 },
buttonState);
}
@ -444,7 +444,7 @@ namespace ControlUnitTests
Log::Comment(L"Scroll up a line, with the left mouse button selected");
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
Core::Point{ 0, WHEEL_DELTA },
cursorPosition1.to_core_point(),
leftMouseDown);
@ -492,55 +492,55 @@ namespace ControlUnitTests
const Core::Point mousePos{ 0, 0 };
Control::MouseButtonState state{};
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 1/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
Log::Comment(L"Scroll up 4 more times. Once we're at 3/5 scrolls, "
L"we'll round the internal scrollbar position to scrolling to the next row.");
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 2/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 3/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 3/5
VERIFY_ARE_EQUAL(20, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 4/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 4/5
VERIFY_ARE_EQUAL(20, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 5/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 5/5
VERIFY_ARE_EQUAL(20, core->ScrollOffset());
Log::Comment(L"Jump to line 5, so we can scroll down from there.");
interactivity->UpdateScrollbar(5);
VERIFY_ARE_EQUAL(5, core->ScrollOffset());
Log::Comment(L"Scroll down 5 times, at which point we should accumulate a whole row of delta.");
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 1/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 1/5
VERIFY_ARE_EQUAL(5, core->ScrollOffset());
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 2/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 2/5
VERIFY_ARE_EQUAL(5, core->ScrollOffset());
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 3/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 3/5
VERIFY_ARE_EQUAL(6, core->ScrollOffset());
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 4/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 4/5
VERIFY_ARE_EQUAL(6, core->ScrollOffset());
interactivity->MouseWheel(modifiers, -delta, mousePos, state); // 5/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, -delta }, mousePos, state); // 5/5
VERIFY_ARE_EQUAL(6, core->ScrollOffset());
Log::Comment(L"Jump to the bottom.");
interactivity->UpdateScrollbar(21);
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
Log::Comment(L"Scroll a bit, then emit a line of text. We should reset our internal scroll position.");
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 1/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 2/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
conn->WriteInput(winrt_wstring_to_array_view(L"Foo\r\n"));
VERIFY_ARE_EQUAL(22, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 1/5
VERIFY_ARE_EQUAL(22, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 2/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 2/5
VERIFY_ARE_EQUAL(22, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 3/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 3/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 4/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 4/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 5/5
interactivity->MouseWheel(modifiers, Core::Point{ 0, delta }, mousePos, state); // 5/5
VERIFY_ARE_EQUAL(21, core->ScrollOffset());
}
@ -709,7 +709,7 @@ namespace ControlUnitTests
{
expectedTop--;
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
Core::Point{ 0, WHEEL_DELTA },
Core::Point{ 0, 0 },
noMouseDown);
}

View File

@ -745,7 +745,7 @@ void AppHost::_RaiseVisualBell(const winrt::Windows::Foundation::IInspectable&,
// - delta: the wheel delta that triggered this event.
// Return Value:
// - <none>
void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta)
void AppHost::_WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const winrt::Microsoft::Terminal::Core::Point delta)
{
if (_windowLogic)
{

View File

@ -73,7 +73,7 @@ private:
void _RaiseVisualBell(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& arg);
void _WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const int32_t delta);
void _WindowMouseWheeled(const winrt::Windows::Foundation::Point coord, const winrt::Microsoft::Terminal::Core::Point delta);
void _WindowActivated(bool activated);
void _WindowMoved();

View File

@ -595,6 +595,7 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
WindowCloseButtonClicked.raise();
return 0;
}
case WM_MOUSEHWHEEL:
case WM_MOUSEWHEEL:
try
{
@ -623,7 +624,11 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
const auto scale = GetCurrentDpiScale();
const winrt::Windows::Foundation::Point real{ relative.x / scale, relative.y / scale };
const auto wheelDelta = static_cast<short>(HIWORD(wparam));
winrt::Microsoft::Terminal::Core::Point wheelDelta{ 0, static_cast<int32_t>(HIWORD(wparam)) };
if (message == WM_MOUSEHWHEEL)
{
std::swap(wheelDelta.X, wheelDelta.Y);
}
// Raise an event, so any listeners can handle the mouse wheel event manually.
MouseScrolled.raise(real, wheelDelta);

View File

@ -74,7 +74,7 @@ public:
til::event<winrt::delegate<>> DragRegionClicked;
til::event<winrt::delegate<>> WindowCloseButtonClicked;
til::event<winrt::delegate<void(winrt::Windows::Foundation::Point, int32_t)>> MouseScrolled;
til::event<winrt::delegate<void(winrt::Windows::Foundation::Point, winrt::Microsoft::Terminal::Core::Point)>> MouseScrolled;
til::event<winrt::delegate<void(bool)>> WindowActivated;
til::event<winrt::delegate<void()>> NotifyNotificationIconPressed;
til::event<winrt::delegate<void()>> NotifyWindowHidden;

View File

@ -567,7 +567,7 @@ BOOL HandleMouseEvent(const SCREEN_INFORMATION& ScreenInfo,
if (!fShiftPressed && !pSelection->IsInSelectingState())
{
short sDelta = 0;
if (Message == WM_MOUSEWHEEL)
if (Message == WM_MOUSEWHEEL || Message == WM_MOUSEHWHEEL)
{
sDelta = GET_WHEEL_DELTA_WPARAM(wParam);
}

View File

@ -127,10 +127,13 @@ public:
wch = L'!';
break;
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
Log::Comment(NoThrowString().Format(L"MOUSEWHEEL"));
wch = L'`' + (sScrollDelta > 0 ? 0 : 1);
break;
case WM_MOUSEHWHEEL:
Log::Comment(NoThrowString().Format(L"MOUSEHWHEEL"));
wch = L'b' + (sScrollDelta > 0 ? 1 : 0);
break;
case WM_MOUSEMOVE:
default:
Log::Comment(NoThrowString().Format(L"DEFAULT"));
@ -168,9 +171,11 @@ public:
result = 3 + 0x20; // we add 0x20 to hover events, which are all encoded as WM_MOUSEMOVE events
break;
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
result = (sScrollDelta > 0 ? 64 : 65);
break;
case WM_MOUSEHWHEEL:
result = (sScrollDelta > 0 ? 67 : 66);
break;
default:
result = 0;
break;
@ -582,6 +587,12 @@ public:
Log::Comment(L"Test mouse wheel scrolling down");
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1B[B"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
Log::Comment(L"Test mouse wheel scrolling right");
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1B[C"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, WHEEL_DELTA, {}));
Log::Comment(L"Test mouse wheel scrolling left");
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1B[D"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
Log::Comment(L"Enable cursor keys mode");
mouseInput.SetInputMode(TerminalInput::Mode::CursorKey, true);
@ -591,6 +602,12 @@ public:
Log::Comment(L"Test mouse wheel scrolling down");
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1BOB"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
Log::Comment(L"Test mouse wheel scrolling right");
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1BOC"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, WHEEL_DELTA, {}));
Log::Comment(L"Test mouse wheel scrolling left");
VERIFY_ARE_EQUAL(TerminalInput::MakeOutput(L"\x1BOD"), mouseInput.HandleMouse({ 0, 0 }, WM_MOUSEHWHEEL, noModifierKeys, -WHEEL_DELTA, {}));
Log::Comment(L"Confirm no effect when scroll mode is disabled");
mouseInput.UseAlternateScreenBuffer();
mouseInput.SetInputMode(TerminalInput::Mode::AlternateScroll, false);

View File

@ -163,9 +163,11 @@ static constexpr wchar_t _windowsButtonToXEncoding(const unsigned int button,
xvalue = 1;
break;
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
xvalue = delta > 0 ? 0x40 : 0x41;
break;
case WM_MOUSEHWHEEL:
xvalue = delta > 0 ? 0x43 : 0x42;
break;
default:
xvalue = 0;
break;
@ -221,9 +223,11 @@ static constexpr int _windowsButtonToSGREncoding(const unsigned int button,
xvalue = 3;
break;
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
xvalue = delta > 0 ? 0x40 : 0x41;
break;
case WM_MOUSEHWHEEL:
xvalue = delta > 0 ? 0x43 : 0x42;
break;
default:
xvalue = 0;
break;
@ -378,7 +382,7 @@ TerminalInput::OutputType TerminalInput::HandleMouse(const til::point position,
if (ShouldSendAlternateScroll(button, delta))
{
return _makeAlternateScrollOutput(delta);
return _makeAlternateScrollOutput(button, delta);
}
return {};
@ -493,7 +497,9 @@ bool TerminalInput::ShouldSendAlternateScroll(const unsigned int button, const s
// - Sends a sequence to the input corresponding to cursor up / down depending on the sScrollDelta.
// Parameters:
// - delta: The scroll wheel delta of the input event
TerminalInput::OutputType TerminalInput::_makeAlternateScrollOutput(const short delta) const
TerminalInput::OutputType TerminalInput::_makeAlternateScrollOutput(const unsigned int button, const short delta) const
{
if (button == WM_MOUSEWHEEL)
{
if (delta > 0)
{
@ -504,3 +510,19 @@ TerminalInput::OutputType TerminalInput::_makeAlternateScrollOutput(const short
return MakeOutput(_keyMap.at(VK_DOWN));
}
}
else if (button == WM_MOUSEHWHEEL)
{
if (delta > 0)
{
return MakeOutput(_keyMap.at(VK_RIGHT));
}
else
{
return MakeOutput(_keyMap.at(VK_LEFT));
}
}
else
{
return {};
}
}

View File

@ -111,7 +111,7 @@ namespace Microsoft::Console::VirtualTerminal
[[nodiscard]] OutputType _GenerateUtf8Sequence(til::point position, unsigned int button, bool isHover, short modifierKeyState, short delta);
[[nodiscard]] OutputType _GenerateSGRSequence(til::point position, unsigned int button, bool isRelease, bool isHover, short modifierKeyState, short delta);
[[nodiscard]] OutputType _makeAlternateScrollOutput(short delta) const;
[[nodiscard]] OutputType _makeAlternateScrollOutput(unsigned int button, short delta) const;
static constexpr unsigned int s_GetPressedButton(const MouseButtonState state) noexcept;
#pragma endregion

View File

@ -897,6 +897,22 @@ bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const VTID id,
eventFlags |= MOUSE_WHEELED;
break;
}
case CsiMouseButtonCodes::ScrollLeft:
{
// set high word to proper scroll direction
// scroll intensity is assumed to be constant value
buttonState |= SCROLL_DELTA_BACKWARD;
eventFlags |= MOUSE_HWHEELED;
break;
}
case CsiMouseButtonCodes::ScrollRight:
{
// set high word to proper scroll direction
// scroll intensity is assumed to be constant value
buttonState |= SCROLL_DELTA_FORWARD;
eventFlags |= MOUSE_HWHEELED;
break;
}
case CsiMouseButtonCodes::Released:
// hover event, we still want to send these but we don't
// need to do anything special here, so just break

View File

@ -111,6 +111,8 @@ namespace Microsoft::Console::VirtualTerminal
Released = 3,
ScrollForward = 4,
ScrollBack = 5,
ScrollLeft = 6,
ScrollRight = 7,
};
constexpr unsigned short CsiMouseModifierCode_Drag = 32;

View File

@ -1196,6 +1196,8 @@ void InputEngineTest::SGRMouseTest_Scroll()
// TEST INPUT EXPECTED OUTPUT
{ { CsiMouseButtonCodes::ScrollForward, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_FORWARD, 0, { 0, 0 }, MOUSE_WHEELED } },
{ { CsiMouseButtonCodes::ScrollBack, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_BACKWARD, 0, { 0, 0 }, MOUSE_WHEELED } },
{ { CsiMouseButtonCodes::ScrollLeft, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_BACKWARD, 0, { 0, 0 }, MOUSE_HWHEELED } },
{ { CsiMouseButtonCodes::ScrollRight, 0, { 1, 1 }, CsiActionCodes::MouseDown }, { SCROLL_DELTA_FORWARD, 0, { 0, 0 }, MOUSE_HWHEELED } },
};
// clang-format on
VerifySGRMouseData(testData);