Index: /trunk/src/VBox/Devices/Input/UsbMouse.cpp
===================================================================
--- /trunk/src/VBox/Devices/Input/UsbMouse.cpp	(revision 43604)
+++ /trunk/src/VBox/Devices/Input/UsbMouse.cpp	(revision 43605)
@@ -147,10 +147,11 @@
      *  is set. */
     RTSEMEVENT          hEvtDoneQueue;
+
     /** Someone is waiting on the done queue. */
     bool                fHaveDoneQueueWaiter;
-
+    /** If device has pending changes. */
+    bool                fHasPendingChanges;    
     /** Is this an absolute pointing device (tablet)? Relative (mouse) otherwise. */
     bool                isAbsolute;
-
     /** Tablet coordinate shift factor for old and broken operating systems. */
     uint8_t             u8CoordShift;
@@ -201,4 +202,13 @@
     uint16_t    cy;
 } USBHIDT_REPORT, *PUSBHIDT_REPORT;
+
+/**
+ * The combined USB HID report union for relative and absolute device.
+ */
+typedef union USBHIDTM_REPORT
+{
+    USBHIDT_REPORT      t;
+    USBHIDM_REPORT      m;
+} USBHIDTM_REPORT, *PUSBHIDTM_REPORT;
 
 /*******************************************************************************
@@ -669,4 +679,5 @@
      */
     pThis->enmState = USBHIDREQSTATE_READY;
+    pThis->fHasPendingChanges = false;
 
     for (unsigned i = 0; i < RT_ELEMENTS(pThis->aEps); i++)
@@ -691,4 +702,71 @@
 }
 
+static int8_t clamp_i8(int32_t val)
+{
+    if (val > 127) {
+        val = 127;
+    } else if (val < -127) {
+        val = -127;
+    }
+    return val;
+}
+
+/**
+ * Create a USB HID report report based on the currently accumulated data.
+ */
+static size_t usbHidFillReport(PUSBHIDTM_REPORT pReport, PUSBHIDM_ACCUM pAccumulated, bool isAbsolute)
+{
+    size_t          cbCopy;
+
+    if (isAbsolute)
+    {
+        pReport->t.btn = pAccumulated->btn;
+        pReport->t.cx  = pAccumulated->dX;
+        pReport->t.cy  = pAccumulated->dY;
+        pReport->t.dz  = clamp_i8(pAccumulated->dZ);
+
+        cbCopy = sizeof(pReport->t);
+//        LogRel(("Abs movement, X=%d, Y=%d, dZ=%d, btn=%02x, report size %d\n", pReport->t.cx, pReport->t.cy, pReport->t.dz, pReport->t.btn, cbCopy));
+    }
+    else
+    {
+        pReport->m.btn = pAccumulated->btn;
+        pReport->m.dx  = clamp_i8(pAccumulated->dX);
+        pReport->m.dy  = clamp_i8(pAccumulated->dY);
+        pReport->m.dz  = clamp_i8(pAccumulated->dZ);
+    
+        cbCopy = sizeof(pReport->m);
+//        LogRel(("Rel movement, dX=%d, dY=%d, dZ=%d, btn=%02x, report size %d\n", pReport->m.dx, pReport->m.dy, pReport->m.dz, pReport->m.btn, cbCopy));
+    }
+
+    /* Clear the accumulated movement. */
+    RT_ZERO(*pAccumulated);
+
+    return cbCopy;
+}
+
+/**
+ * Sends a state report to the host if there is a pending URB.
+ */
+static int usbHidSendReport(PUSBHID pThis)
+{
+    PVUSBURB    pUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue);
+
+    if (pUrb)
+    {
+        PUSBHIDTM_REPORT    pReport = (PUSBHIDTM_REPORT)&pUrb->abData[0];
+        size_t              cbCopy;
+
+        cbCopy = usbHidFillReport(pReport, &pThis->PtrDelta, pThis->isAbsolute);
+        pThis->fHasPendingChanges = false;
+        return usbHidCompleteOk(pThis, pUrb, cbCopy);
+    }
+    else
+    {
+        Log2(("No available URB for USB mouse\n"));
+        pThis->fHasPendingChanges = true;
+    }
+    return VINF_EOF;
+}
 
 /**
@@ -701,14 +779,4 @@
     PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Lun0.IPort);
     return NULL;
-}
-
-static int8_t clamp_i8(int32_t val)
-{
-    if (val > 127) {
-        val = 127;
-    } else if (val < -127) {
-        val = -127;
-    }
-    return val;
 }
 
@@ -727,10 +795,5 @@
 {
     PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort);
-//    int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
-//    AssertReleaseRC(rc);
-
-    /* If we aren't in the expected mode, switch. This should only really need to be done once. */
-//    if (pThis->isAbsolute)
-//        pThis->Lun0.pDrv->pfnAbsModeChange(pThis->Lun0.pDrv, pThis->isAbsolute);
+    RTCritSectEnter(&pThis->CritSect);
 
     /* Accumulate movement - the events from the front end may arrive
@@ -742,30 +805,8 @@
     pThis->PtrDelta.dZ -= i32DeltaZ;    /* Inverted! */
 
-    /* Check if there's a URB waiting. If so, send a report.
-     */
-    PVUSBURB pUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue);
-    if (pUrb)
-    {
-        size_t          cbCopy;
-        USBHIDM_REPORT  report;
-
-        //@todo: fix/extend
-        report.btn = pThis->PtrDelta.btn;
-        report.dx  = clamp_i8(pThis->PtrDelta.dX);
-        report.dy  = clamp_i8(pThis->PtrDelta.dY);
-        report.dz  = clamp_i8(pThis->PtrDelta.dZ);
-
-        cbCopy = sizeof(report);
-        memcpy(&pUrb->abData[0], &report, cbCopy);
-
-        /* Clear the accumulated movement. */
-        pThis->PtrDelta.dX = pThis->PtrDelta.dY = pThis->PtrDelta.dZ = 0;
-
-        /* Complete the URB. */
-        usbHidCompleteOk(pThis, pUrb, cbCopy);
-//        LogRel(("Rel movement, dX=%d, dY=%d, dZ=%d, btn=%02x, report size %d\n", report.dx, report.dy, report.dz, report.btn, cbCopy));
-    }
-
-//    PDMCritSectLeave(&pThis->CritSect);
+    /* Send a report if possible. */
+    usbHidSendReport(pThis);
+
+    RTCritSectLeave(&pThis->CritSect);
     return VINF_SUCCESS;
 }
@@ -785,6 +826,5 @@
 {
     PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort);
-//    int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
-//    AssertReleaseRC(rc);
+    RTCritSectEnter(&pThis->CritSect);
 
     Assert(pThis->isAbsolute);
@@ -792,34 +832,16 @@
     /* Accumulate movement - the events from the front end may arrive
      * at a much higher rate than USB can handle. Probably not a real issue
-     * when only the Z axis is relative.
+     * when only the Z axis is relative (X/Y movement isn't technically
+     * accumulated and only the last value is used).
      */
     pThis->PtrDelta.btn = fButtonStates;
+    pThis->PtrDelta.dX  = u32X >> pThis->u8CoordShift;
+    pThis->PtrDelta.dY  = u32Y >> pThis->u8CoordShift;
     pThis->PtrDelta.dZ -= i32DeltaZ;    /* Inverted! */
 
-    /* Check if there's a URB waiting. If so, send a report.
-     */
-    PVUSBURB pUrb = usbHidQueueRemoveHead(&pThis->ToHostQueue);
-    if (pUrb)
-    {
-        size_t          cbCopy;
-        USBHIDT_REPORT  report;
-
-        report.btn = pThis->PtrDelta.btn;
-        report.cx  = u32X >> pThis->u8CoordShift;
-        report.cy  = u32Y >> pThis->u8CoordShift;
-        report.dz  = clamp_i8(pThis->PtrDelta.dZ);
-
-        cbCopy = sizeof(report);
-        memcpy(&pUrb->abData[0], &report, cbCopy);
-
-        /* Clear the accumulated movement. */
-        pThis->PtrDelta.dZ = 0;
-
-        /* Complete the URB. */
-        usbHidCompleteOk(pThis, pUrb, cbCopy);
-//        LogRel(("Abs movement, X=%d, Y=%d, dZ=%d, btn=%02x, report size %d\n", report.cx, report.cy, report.dz, report.btn, cbCopy));
-    }
-
-//    PDMCritSectLeave(&pThis->CritSect);
+    /* Send a report if possible. */
+    usbHidSendReport(pThis);
+
+    RTCritSectLeave(&pThis->CritSect);
     return VINF_SUCCESS;
 }
@@ -919,4 +941,7 @@
         case USBHIDREQSTATE_READY:
             usbHidQueueAddTail(&pThis->ToHostQueue, pUrb);
+            /* If a report is pending, send it right away. */
+            if (pThis->fHasPendingChanges)
+                usbHidSendReport(pThis);
             LogFlow(("usbHidHandleIntrDevToHost: Added %p:%s to the queue\n", pUrb, pUrb->pszDesc));
             return VINF_SUCCESS;
