Skip to content

Commit

Permalink
- Fix legacy input state getting out of sync when raw mouse input gra…
Browse files Browse the repository at this point in the history
…bs or releases the events

- Remove the need to center the mouse by specifying RIDEV_INPUTSINK (RIDEV_CAPTUREMOUSE does not take effect unless it is also an input sink)
  • Loading branch information
dpjudas committed Mar 29, 2020
1 parent 43eb262 commit b4424b2
Showing 1 changed file with 117 additions and 27 deletions.
144 changes: 117 additions & 27 deletions src/win32/i_mouse.cpp
Expand Up @@ -78,6 +78,15 @@ class FRawMouse : public FMouse
protected:
bool Grabbed;
POINT UngrabbedPointerPos;

private:
void RegisterRawInput();
void UnregisterRawInput();
void CheckDelayedUnregister(RAWINPUT* raw);
void ReleaseLegacyInput();

bool mDelayUnregister = false;
int mButtonsDown = 0;
};

class FDInputMouse : public FMouse
Expand Down Expand Up @@ -569,22 +578,21 @@ void FRawMouse::Grab()
{
if (!Grabbed)
{
RAWINPUTDEVICE rid;

rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY;
rid.hwndTarget = Window;
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
if (!mDelayUnregister)
{
ReleaseLegacyInput();
RegisterRawInput();
mButtonsDown = 0;
}
else
{
GetCursorPos(&UngrabbedPointerPos);
Grabbed = true;
SetCursorState(false);
// By setting the cursor position, we force the pointer image
// to change right away instead of having it delayed until
// some time in the future.
CenterMouse(-1, -1, NULL, NULL);
mDelayUnregister = false;
}

GetCursorPos(&UngrabbedPointerPos);
while (ShowCursor(FALSE) >= 0);
Grabbed = true;
SetCursorState(false);
}
}

Expand All @@ -598,19 +606,104 @@ void FRawMouse::Ungrab()
{
if (Grabbed)
{
RAWINPUTDEVICE rid;

rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
// This is to prevent WM_RBUTTONUP from falling through to the application under the cursor when we release capture.
if (mButtonsDown == 0)
{
UnregisterRawInput();
}
else
{
Grabbed = false;
ClearButtonState();
mDelayUnregister = true;
}

Grabbed = false;
ClearButtonState();
SetCursorState(true);
SetCursorPos(UngrabbedPointerPos.x, UngrabbedPointerPos.y);
while (ShowCursor(TRUE) < 0);
}
}

//==========================================================================
//
// FRawMouse :: RegisterRawInput
//
//==========================================================================

void FRawMouse::RegisterRawInput()
{
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY | RIDEV_INPUTSINK;
rid.hwndTarget = Window;
RegisterRawInputDevices(&rid, 1, sizeof(rid));
}

//==========================================================================
//
// FRawMouse :: UnregisterRawInput
//
//==========================================================================

void FRawMouse::UnregisterRawInput()
{
RAWINPUTDEVICE rid;
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_REMOVE;
rid.hwndTarget = NULL;
RegisterRawInputDevices(&rid, 1, sizeof(rid));
}

//==========================================================================
//
// FRawMouse :: ReleaseLegacyMouseDown
//
//==========================================================================

void FRawMouse::ReleaseLegacyInput()
{
// Send release of all pressed mouse buttons to the Windows legacy input system.
// If this isn't done the legacy input system may think the buttons are still down when we release the capture of input events.

const int vkeys[] = { VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_XBUTTON1, VK_XBUTTON2 };
const DWORD keyflags[] = { MOUSEEVENTF_LEFTUP, MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_XUP, MOUSEEVENTF_XUP };
const DWORD mousedata[] = { 0, 0, 0, XBUTTON1, XBUTTON2 };
std::vector<INPUT> inputs;
for (int i = 0; i < 5; i++)
{
bool keydown = GetKeyState(vkeys[i]) < 0;
if (keydown)
{
INPUT input = {};
input.type = INPUT_MOUSE;
input.mi.dwExtraInfo = GetMessageExtraInfo();
input.mi.dwFlags = keyflags[i];
input.mi.mouseData = mousedata[i];
inputs.push_back(input);
}
}
SendInput((UINT)inputs.size(), inputs.data(), sizeof(INPUT));
}

void FRawMouse::CheckDelayedUnregister(RAWINPUT* raw)
{
// Track button state
for (DWORD i = 0; i < 5; i++)
{
DWORD down = 1 << (i * 2);
DWORD up = down << 1;
if (raw->data.mouse.usButtonFlags & down)
mButtonsDown |= (1 << i);
else if (raw->data.mouse.usButtonFlags & up)
mButtonsDown &= ~(1 << i);
}

if (mDelayUnregister && mButtonsDown == 0)
{
UnregisterRawInput();
mDelayUnregister = false;
}
}

Expand Down Expand Up @@ -655,10 +748,7 @@ bool FRawMouse::ProcessRawInput(RAWINPUT *raw, int code)
int x = m_noprescale ? raw->data.mouse.lLastX : raw->data.mouse.lLastX << 2;
int y = -raw->data.mouse.lLastY;
PostMouseMove(x, y);
if (x | y)
{
CenterMouse(-1, -1, NULL, NULL);
}
CheckDelayedUnregister(raw);
return true;
}

Expand Down

0 comments on commit b4424b2

Please sign in to comment.