Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass through double clicks and hover events in Win32 mouse mode #10138

Merged
15 commits merged into from
May 24, 2021
1 change: 1 addition & 0 deletions .github/actions/spelling/allow/apis.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ CYICON
dataobject
dcomp
DERR
difftime
dlldata
DONTADDTORECENT
DWORDLONG
Expand Down
32 changes: 24 additions & 8 deletions src/terminal/parser/InputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ InputStateMachineEngine::InputStateMachineEngine(std::unique_ptr<IInteractDispat
InputStateMachineEngine::InputStateMachineEngine(std::unique_ptr<IInteractDispatch> pDispatch, const bool lookingForDSR) :
_pDispatch(std::move(pDispatch)),
_lookingForDSR(lookingForDSR),
_pfnFlushToInputQueue(nullptr)
_pfnFlushToInputQueue(nullptr),
_doubleClickTime(GetDoubleClickTime()),
_lastMouseClickTime(time(nullptr))
PankajBhojwani marked this conversation as resolved.
Show resolved Hide resolved
{
THROW_HR_IF_NULL(E_INVALIDARG, _pDispatch.get());
}
Expand Down Expand Up @@ -386,9 +388,11 @@ bool InputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParameter
DWORD buttonState = 0;
DWORD eventFlags = 0;
const size_t firstParameter = parameters.at(0).value_or(0);
const COORD uiPos{ gsl::narrow<short>(parameters.at(1)) - 1, gsl::narrow<short>(parameters.at(2)) - 1 };
PankajBhojwani marked this conversation as resolved.
Show resolved Hide resolved

modifierState = _GetSGRMouseModifierState(firstParameter);
success = _UpdateSGRMouseButtonState(id, firstParameter, buttonState, eventFlags);
success = success && _WriteMouseEvent(parameters.at(1), parameters.at(2), buttonState, modifierState, eventFlags);
success = _UpdateSGRMouseButtonState(id, firstParameter, buttonState, eventFlags, uiPos);
success = success && _WriteMouseEvent(uiPos, buttonState, modifierState, eventFlags);
break;
}
// case CsiActionCodes::DSR_DeviceStatusReportResponse:
Expand Down Expand Up @@ -723,10 +727,8 @@ bool InputStateMachineEngine::_WriteSingleKey(const short vkey, const DWORD modi
// - eventFlags - the type of mouse event to write to the mouse record.
// Return Value:
// - true iff we successfully wrote the keypress to the input callback.
bool InputStateMachineEngine::_WriteMouseEvent(const size_t column, const size_t line, const DWORD buttonState, const DWORD controlKeyState, const DWORD eventFlags)
bool InputStateMachineEngine::_WriteMouseEvent(const COORD uiPos, const DWORD buttonState, const DWORD controlKeyState, const DWORD eventFlags)
{
COORD uiPos = { gsl::narrow<short>(column) - 1, gsl::narrow<short>(line) - 1 };

INPUT_RECORD rgInput;
rgInput.EventType = MOUSE_EVENT;
rgInput.Event.MouseEvent.dwMousePosition = uiPos;
Expand Down Expand Up @@ -846,7 +848,8 @@ DWORD InputStateMachineEngine::_GetModifier(const size_t modifierParam) noexcept
bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const VTID id,
const size_t sgrEncoding,
DWORD& buttonState,
DWORD& eventFlags) noexcept
DWORD& eventFlags,
COORD uiPos) noexcept
{
// Starting with the state from the last mouse event we received
buttonState = _mouseButtonState;
Expand All @@ -862,7 +865,7 @@ bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const VTID id,
// This retrieves the 2 MSBs and concatenates them to the 2 LSBs to create BBBB in binary
// This represents which button had a change in state
const auto buttonID = (sgrEncoding & 0x3) | ((sgrEncoding & 0xC0) >> 4);

const auto currentTime = time(nullptr);
// Step 1: Translate which button was affected
// NOTE: if scrolled, having buttonFlag = 0 means
// we don't actually update the buttonState
Expand All @@ -871,6 +874,19 @@ bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const VTID id,
{
case CsiMouseButtonCodes::Left:
buttonFlag = FROM_LEFT_1ST_BUTTON_PRESSED;
// If this is a mouse down, check if it's a double click
// and also update our last clicked position and time
if (id == CsiActionCodes::MouseDown)
{
// difftime returns in seconds but double click time returns in milliseconds
if (til::point(uiPos) == _lastMouseClickPos &&
(difftime(currentTime, _lastMouseClickTime) * 1000) < _doubleClickTime)
{
eventFlags |= DOUBLE_CLICK;
}
_lastMouseClickPos = uiPos;
PankajBhojwani marked this conversation as resolved.
Show resolved Hide resolved
_lastMouseClickTime = currentTime;
}
break;
case CsiMouseButtonCodes::Right:
buttonFlag = RIGHTMOST_BUTTON_PRESSED;
Expand Down
8 changes: 6 additions & 2 deletions src/terminal/parser/InputStateMachineEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ namespace Microsoft::Console::VirtualTerminal
std::function<bool()> _pfnFlushToInputQueue;
bool _lookingForDSR;
DWORD _mouseButtonState = 0;
COORD _lastMouseClickPos{ 0, 0 };
uint64_t _doubleClickTime;
std::time_t _lastMouseClickTime;

DWORD _GetCursorKeysModifierState(const VTParameters parameters, const VTID id) noexcept;
DWORD _GetGenericKeysModifierState(const VTParameters parameters) noexcept;
Expand All @@ -181,15 +184,16 @@ namespace Microsoft::Console::VirtualTerminal
bool _UpdateSGRMouseButtonState(const VTID id,
const size_t sgrEncoding,
DWORD& buttonState,
DWORD& eventFlags) noexcept;
DWORD& eventFlags,
COORD uiPos) noexcept;
bool _GetGenericVkey(const GenericKeyIdentifiers identifier, short& vkey) const;
bool _GetCursorKeysVkey(const VTID id, short& vkey) const;
bool _GetSs3KeysVkey(const wchar_t wch, short& vkey) const;

bool _WriteSingleKey(const short vkey, const DWORD modifierState);
bool _WriteSingleKey(const wchar_t wch, const short vkey, const DWORD modifierState);

bool _WriteMouseEvent(const size_t column, const size_t line, const DWORD buttonState, const DWORD controlKeyState, const DWORD eventFlags);
bool _WriteMouseEvent(const COORD uiPos, const DWORD buttonState, const DWORD controlKeyState, const DWORD eventFlags);

void _GenerateWrappedSequence(const wchar_t wch,
const short vkey,
Expand Down