Index: /trunk/src/VBox/Devices/Input/PS2M.cpp
===================================================================
--- /trunk/src/VBox/Devices/Input/PS2M.cpp	(revision 68088)
+++ /trunk/src/VBox/Devices/Input/PS2M.cpp	(revision 68089)
@@ -832,4 +832,11 @@
 #ifdef IN_RING3
 
+/** Is there any state change to send as events to the guest? */
+static uint32_t ps2mHaveEvents(PPS2M pThis)
+{
+    return   pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ
+           | (pThis->fCurrB != pThis->fReportedB) | (pThis->fAccumB != 0);
+}
+
 /* Event rate throttling timer to emulate the auxiliary device sampling rate.
  */
@@ -848,5 +855,5 @@
 #else
     /* If more movement is accumulated, report it and restart the timer. */
-    uHaveEvents = pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ | (pThis->fCurrB != pThis->fReportedB);
+    uHaveEvents = ps2mHaveEvents(pThis);
     LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));
 
@@ -991,4 +998,66 @@
 }
 
+#ifdef RT_STRICT
+/** Test the event accumulation mechanism which we use to delay events going
+ * to the guest to one per 50ms.  This test depends on ps2mPutEventWorker() not
+ * touching the timer if This.fThrottleActive is true. */
+/** @todo if we add any more tests it might be worth using a table of test
+ * operations and checks. */
+static void ps2mTestAccumulation(void)
+{
+    PS2M This;
+    unsigned i;
+    int rc;
+    uint8_t b;
+
+    RT_ZERO(This);
+    This.evtQ.cSize = AUX_EVT_QUEUE_SIZE;
+    This.u8State = AUX_STATE_ENABLED;
+    This.fThrottleActive = true;
+    /* Certain Windows touch pad drivers report a double tap as a press, then
+     * a release-press-release all within a single 50ms interval.  Simulate
+     * this to check that it is handled right. */
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 0);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 0);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    for (i = 0; i < 12; ++i)
+    {
+        const uint8_t abExpected[] = { 9, 0, 0, 8, 0, 0, 9, 0, 0, 8, 0, 0};
+
+        rc = PS2MByteFromAux(&This, &b);
+        AssertRCSuccess(rc);
+        Assert(b == abExpected[i]);
+    }
+    rc = PS2MByteFromAux(&This, &b);
+    Assert(rc != VINF_SUCCESS);
+    /* Button hold down during mouse drags was broken at some point during
+     * testing fixes for the previous issue.  Test that that works. */
+    ps2mPutEventWorker(&This, 0, 0, 0, 0, 1);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    if (ps2mHaveEvents(&This))
+        ps2mReportAccumulatedEvents(&This, (GeneriQ *)&This.evtQ, true);
+    for (i = 0; i < 3; ++i)
+    {
+        const uint8_t abExpected[] = { 9, 0, 0 };
+
+        rc = PS2MByteFromAux(&This, &b);
+        AssertRCSuccess(rc);
+        Assert(b == abExpected[i]);
+    }
+    rc = PS2MByteFromAux(&This, &b);
+    Assert(rc != VINF_SUCCESS);
+}
+#endif /* RT_STRICT */
+
 /* -=-=-=-=-=- Mouse: IMousePort  -=-=-=-=-=- */
 
@@ -1186,4 +1255,8 @@
     LogFlowFunc(("iInstance=%d\n", iInstance));
 
+#ifdef RT_STRICT
+    ps2mTestAccumulation();
+#endif
+
     pThis->pParent = pParent;
 
