From fa267ecbb332f6d88b578e526b92766a9e2c84ab Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Thu, 24 Oct 2024 15:12:48 +0200 Subject: [PATCH] vbox6: robust tracking of mouse events in VMMDev In VirtualBox 7 and recent 6.1 versions, the VMMDev is used to report all mouse events if guest additions are used. Therefore, the implementation aggregates these events, notifies the guest. and passes the state on guest request. Unfortunately, the protocol does not support to report consecutive button press and release events that may happen between two guest requests, which results in events getting lost. This commit patches the contrib sources to track pending mouse-button events and notifies the guest if further state changes are pending after updates are delivered. Also, mouse-wheel events (dw, dz) are now accumulated between two guest updates. Fixes #5333 --- repos/ports/ports/virtualbox6.hash | 2 +- repos/ports/src/virtualbox6/main.cc | 2 +- .../virtualbox6/patches/mousebuttons.patch | 189 ++++++++++++++++++ repos/ports/src/virtualbox6/patches/series | 1 + 4 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 repos/ports/src/virtualbox6/patches/mousebuttons.patch diff --git a/repos/ports/ports/virtualbox6.hash b/repos/ports/ports/virtualbox6.hash index 6ff69bd74b..cf83bd1ff0 100644 --- a/repos/ports/ports/virtualbox6.hash +++ b/repos/ports/ports/virtualbox6.hash @@ -1 +1 @@ -969705fd5573cd8f85c9023e06a61bf2cb3cc37c +4fe7913734f0dbabae9fae0684e34fe1fe60e8b2 diff --git a/repos/ports/src/virtualbox6/main.cc b/repos/ports/src/virtualbox6/main.cc index 62308f8cf8..411fd3f02b 100644 --- a/repos/ports/src/virtualbox6/main.cc +++ b/repos/ports/src/virtualbox6/main.cc @@ -411,7 +411,7 @@ void Main::_sync_capslock() void Main::_handle_input() { auto handle_one_event = [&] (Input::Event const &ev) { - /* don't confuse guests and drop CapsLock events in ROM mode */ + /* don't confuse guests and drop CapsLock events in ROM mode */ if (_capslock.mode == Capslock::Mode::ROM) { if (ev.key_press(Input::KEY_CAPSLOCK) || ev.key_release(Input::KEY_CAPSLOCK)) diff --git a/repos/ports/src/virtualbox6/patches/mousebuttons.patch b/repos/ports/src/virtualbox6/patches/mousebuttons.patch new file mode 100644 index 0000000000..2a50a0a16d --- /dev/null +++ b/repos/ports/src/virtualbox6/patches/mousebuttons.patch @@ -0,0 +1,189 @@ +--- a/src/virtualbox6/src/VBox/Devices/VMMDev/VMMDev.cpp ++++ b/src/virtualbox6/src/VBox/Devices/VMMDev/VMMDev.cpp +@@ -150,6 +150,90 @@ + #define VMMDEV_HEARTBEAT_DEFAULT_INTERVAL (2U*RT_NS_1SEC_64) + + ++/* ++ * Mouse-button event tracking ++ * ++ * If mouse press and release events are reported in quick succession (e.g., ++ * with Genode detection enabled), the guest may miss events if ++ * fButtons is not collected quick enough. ++ */ ++ ++bool VMMDEV::MouseButtons::Button::pending() const ++{ ++ return _pending != Pending::NONE; ++} ++ ++bool VMMDEV::MouseButtons::Button::record(bool const press) ++{ ++ /* ++ * We begin recording only if the state changes, but keep recording ++ * afterwards until nothing is pending anymore. ++ */ ++ if (_pending == Pending::NONE && _pressed == press) ++ return false; ++ ++ switch (_pending) { ++ case Pending::NONE: _pending = press ? Pending::PRESS : Pending::RELEASE; break; ++ case Pending::PRESS: _pending = press ? Pending::PRESS : Pending::PRESS_RELEASE; break; ++ case Pending::PRESS_RELEASE: _pending = press ? Pending::PRESS : Pending::PRESS_RELEASE; break; ++ case Pending::RELEASE: _pending = !press ? Pending::RELEASE : Pending::RELEASE_PRESS; break; ++ case Pending::RELEASE_PRESS: _pending = !press ? Pending::RELEASE : Pending::RELEASE_PRESS; break; ++ } ++ ++ return true; ++} ++ ++bool VMMDEV::MouseButtons::Button::next() ++{ ++ switch (_pending) { ++ case Pending::NONE: break; ++ ++ case Pending::PRESS: _pending = Pending::NONE; _pressed = true; break; ++ case Pending::PRESS_RELEASE: _pending = Pending::RELEASE; _pressed = true; break; ++ case Pending::RELEASE: _pending = Pending::NONE; _pressed = false; break; ++ case Pending::RELEASE_PRESS: _pending = Pending::PRESS; _pressed = false; break; ++ } ++ ++ return _pressed; ++} ++ ++bool VMMDEV::MouseButtons::pending() const ++{ ++ return l.pending() || r.pending() || m.pending() || x1.pending() || x2.pending(); ++} ++ ++void VMMDEV::MouseButtons::clear() ++{ ++ l = r = m = x1 = x2 = { Pending::NONE, 0 }; ++} ++ ++bool VMMDEV::MouseButtons::record(unsigned fButtons) ++{ ++ bool pending = false; ++ ++ pending |= l.record(!!(fButtons & VMMDEV_MOUSE_BUTTON_LEFT)); ++ pending |= r.record(!!(fButtons & VMMDEV_MOUSE_BUTTON_RIGHT)); ++ pending |= m.record(!!(fButtons & VMMDEV_MOUSE_BUTTON_MIDDLE)); ++ pending |= x1.record(!!(fButtons & VMMDEV_MOUSE_BUTTON_X1)); ++ pending |= x2.record(!!(fButtons & VMMDEV_MOUSE_BUTTON_X2)); ++ ++ return pending; ++} ++ ++unsigned VMMDEV::MouseButtons::next() ++{ ++ unsigned next = 0; ++ ++ next |= l.next() ? VMMDEV_MOUSE_BUTTON_LEFT : 0; ++ next |= r.next() ? VMMDEV_MOUSE_BUTTON_RIGHT : 0; ++ next |= m.next() ? VMMDEV_MOUSE_BUTTON_MIDDLE : 0; ++ next |= x1.next() ? VMMDEV_MOUSE_BUTTON_X1 : 0; ++ next |= x2.next() ? VMMDEV_MOUSE_BUTTON_X2 : 0; ++ ++ return next; ++} ++ ++ + #ifndef VBOX_DEVICE_STRUCT_TESTCASE + #ifdef IN_RING3 + +@@ -1079,7 +1163,7 @@ + * @param pThis The VMMDev shared instance data. + * @param pReqHdr The header of the request to handle. + */ +-static int vmmdevReqHandler_GetMouseStatusEx(PVMMDEV pThis, VMMDevRequestHeader *pReqHdr) ++static int vmmdevReqHandler_GetMouseStatusEx(PPDMDEVINS pDevIns, PVMMDEV pThis, PVMMDEVCC pThisCC, VMMDevRequestHeader *pReqHdr) + { + VMMDevReqMouseStatusEx *pReq = (VMMDevReqMouseStatusEx *)pReqHdr; + AssertMsgReturn(pReq->Core.header.size == sizeof(*pReq), ("%u\n", pReq->Core.header.size), VERR_INVALID_PARAMETER); +@@ -1099,7 +1183,14 @@ + pReq->Core.pointerYPos = pThis->yMouseAbs; + pReq->dz = pThis->dzMouse; + pReq->dw = pThis->dwMouse; +- pReq->fButtons = pThis->fMouseButtons; ++ ++ /* accumulated relative wheel motion consumed */ ++ pThis->dzMouse = 0; pThis->dwMouse = 0; ++ ++ pReq->fButtons = pThis->mouseButtons.next(); ++ if (pThis->mouseButtons.pending()) ++ vmmdevNotifyGuestWorker(pDevIns, pThis, pThisCC, VMMDEV_EVENT_MOUSE_POSITION_CHANGED); ++ + LogRel2(("VMMDev: vmmdevReqHandler_GetMouseStatusEx: mouseFeatures=%#x, xAbs=%d, yAbs=%d, zAbs=%d, wMouseRel=%d, fButtons=0x%x\n", + pReq->Core.mouseFeatures, pReq->Core.pointerXPos, pReq->Core.pointerYPos, pReq->dz, pReq->dw, pReq->fButtons)); + return VINF_SUCCESS; +@@ -2753,7 +2844,7 @@ + break; + + case VMMDevReq_GetMouseStatusEx: +- pReqHdr->rc = vmmdevReqHandler_GetMouseStatusEx(pThis, pReqHdr); ++ pReqHdr->rc = vmmdevReqHandler_GetMouseStatusEx(pDevIns, pThis, pThisCC, pReqHdr); + break; + + case VMMDevReq_SetMouseStatus: +@@ -3588,16 +3679,17 @@ + || pThis->yMouseAbs != yAbs + || dz + || dw +- || pThis->fMouseButtons != fButtons) ++ || pThis->mouseButtons.record(fButtons)) + { + Log2(("vmmdevIPort_SetAbsoluteMouse : settings absolute position to x = %d, y = %d, z = %d, w = %d, fButtons = 0x%x\n", + xAbs, yAbs, dz, dw, fButtons)); + + pThis->xMouseAbs = xAbs; + pThis->yMouseAbs = yAbs; +- pThis->dzMouse = dz; +- pThis->dwMouse = dw; +- pThis->fMouseButtons = fButtons; ++ ++ /* accumulate relative wheel motion */ ++ pThis->dzMouse += dz; ++ pThis->dwMouse += dw; + + VMMDevNotifyGuest(pDevIns, pThis, pThisCC, VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + } +@@ -4114,7 +4206,7 @@ + * into saved state, but rather initialize to zeroes on load. */ + pThis->dzMouse = 0; + pThis->dwMouse = 0; +- pThis->fMouseButtons = 0; ++ pThis->mouseButtons.clear(); + + pHlp->pfnSSMGetBool(pSSM, &pThis->fNewGuestFilterMaskValid); + pHlp->pfnSSMGetU32(pSSM, &pThis->fNewGuestFilterMask); +--- a/src/virtualbox6/src/VBox/Devices/VMMDev/VMMDevState.h ++++ b/src/virtualbox6/src/VBox/Devices/VMMDev/VMMDevState.h +@@ -134,7 +134,27 @@ + int32_t yMouseAbs; + int32_t dzMouse; + int32_t dwMouse; +- uint32_t fMouseButtons; ++ ++ struct MouseButtons ++ { ++ enum class Pending { NONE, PRESS, PRESS_RELEASE, RELEASE, RELEASE_PRESS }; ++ ++ struct Button ++ { ++ Pending _pending; ++ bool _pressed; ++ ++ bool pending() const; ++ bool record(bool state); ++ bool next(); ++ } l, r, m, x1, x2; ++ ++ bool pending() const; ++ void clear(); ++ bool record(unsigned fButtons); ++ unsigned next(); ++ } mouseButtons; ++ + /** @} */ + /** Does the guest currently want the host pointer to be shown? */ + uint32_t fHostCursorRequested; diff --git a/repos/ports/src/virtualbox6/patches/series b/repos/ports/src/virtualbox6/patches/series index b879675db0..4dba77e48f 100644 --- a/repos/ports/src/virtualbox6/patches/series +++ b/repos/ports/src/virtualbox6/patches/series @@ -16,3 +16,4 @@ disk_geometry.patch stack_size.patch avx.patch rdrand.patch +mousebuttons.patch