Index: /trunk/src/VBox/Devices/Input/UsbMouse.cpp
===================================================================
--- /trunk/src/VBox/Devices/Input/UsbMouse.cpp	(revision 26681)
+++ /trunk/src/VBox/Devices/Input/UsbMouse.cpp	(revision 26682)
@@ -32,5 +32,6 @@
  * @{ */
 #define USBHID_STR_ID_MANUFACTURER  1
-#define USBHID_STR_ID_PRODUCT       2
+#define USBHID_STR_ID_PRODUCT_M     2
+#define USBHID_STR_ID_PRODUCT_T     3
 /** @} */
 
@@ -44,4 +45,5 @@
 #define VBOX_USB_VENDOR             0x80EE
 #define USBHID_PID_MOUSE            0x0020
+#define USBHID_PID_TABLET           0x0021
 /** @} */
 
@@ -142,4 +144,7 @@
     bool                fHaveDoneQueueWaiter;
 
+    /** Is this an absolute pointing device (tablet)? Relative (mouse) otherwise. */
+    bool                isAbsolute;
+
     /**
      * Mouse port - LUN#0.
@@ -166,5 +171,5 @@
 
 /**
- * The USB HID report structure.
+ * The USB HID report structure for relative device.
  */
 typedef struct USBHIDM_REPORT
@@ -176,4 +181,16 @@
 } USBHIDM_REPORT, *PUSBHIDM_REPORT;
 
+/**
+ * The USB HID report structure for relative device.
+ */
+
+typedef struct USBHIDT_REPORT
+{
+    uint16_t    cx;
+    uint16_t    cy;
+    int8_t      dz;
+    uint8_t     btn;
+} USBHIDT_REPORT, *PUSBHIDT_REPORT;
+
 /*******************************************************************************
 *   Global Variables                                                           *
@@ -182,5 +199,6 @@
 {
     { USBHID_STR_ID_MANUFACTURER,   "VirtualBox"    },
-    { USBHID_STR_ID_PRODUCT,        "USB Mouse"     },
+    { USBHID_STR_ID_PRODUCT_M,      "USB Mouse"     },
+    { USBHID_STR_ID_PRODUCT_T,      "USB Tablet"    },
 };
 
@@ -190,5 +208,5 @@
 };
 
-static const VUSBDESCENDPOINTEX g_aUsbHidEndpointDescs[] =
+static const VUSBDESCENDPOINTEX g_aUsbHidMEndpointDescs[] =
 {
     {
@@ -207,6 +225,23 @@
 };
 
-/* HID report descriptor. */
-static const uint8_t g_UsbHidReportDesc[] =
+static const VUSBDESCENDPOINTEX g_aUsbHidTEndpointDescs[] =
+{
+    {
+        {
+            /* .bLength = */            sizeof(VUSBDESCENDPOINT),
+            /* .bDescriptorType = */    VUSB_DT_ENDPOINT,
+            /* .bEndpointAddress = */   0x81 /* ep=1, in */,
+            /* .bmAttributes = */       3 /* interrupt */,
+            /* .wMaxPacketSize = */     6,
+            /* .bInterval = */          10,
+        },
+        /* .pvMore = */     NULL,
+        /* .pvClass = */    NULL,
+        /* .cbClass = */    0
+    },
+};
+
+/* HID report descriptor (mouse). */
+static const uint8_t g_UsbHidMReportDesc[] =
 {
     /* Usage Page */                0x05, 0x01,     /* Generic Desktop */
@@ -239,6 +274,46 @@
 };
 
+/* HID report descriptor (tablet). */
+static const uint8_t g_UsbHidTReportDesc[] =
+{
+    /* Usage Page */                0x05, 0x01,     /* Generic Desktop */
+    /* Usage */                     0x09, 0x02,     /* Mouse */
+    /* Collection */                0xA1, 0x01,     /* Application */
+    /* Usage */                     0x09, 0x01,     /* Pointer */
+    /* Collection */                0xA1, 0x00,     /* Physical */
+    /* Usage Page */                0x05, 0x01,     /* Generic Desktop */
+    /* Usage */                     0x09, 0x30,     /* X */
+    /* Usage */                     0x09, 0x31,     /* Y */
+    /* Logical Minimum */           0x15, 0x00,     /* 0 */
+    /* Logical Maximum */           0x26, 0xFF,0x7F,/* 0x7fff */
+    /* Physical Minimum */          0x35, 0x00,     /* 0 */
+    /* Physical Maximum */          0x46, 0xFF,0x7F,/* 0x7fff */
+    /* Report Size */               0x75, 0x10,     /* 16 */
+    /* Report Count */              0x95, 0x02,     /* 2 */
+    /* Input */                     0x81, 0x02,     /* Data, Value, Absolute, Bit field */
+    /* Usage Page */                0x05, 0x01,     /* Generic Desktop */
+    /* Usage */                     0x09, 0x38,     /* Z (wheel) */
+    /* Logical Minimum */           0x15, 0x81,     /* -127 */
+    /* Logical Maximum */           0x25, 0x7F,     /* +127 */
+    /* Report Size */               0x75, 0x08,     /* 8 */
+    /* Report Count */              0x95, 0x01,     /* 1 */
+    /* Input */                     0x81, 0x06,     /* Data, Value, Relative, Bit field */
+    /* Usage Page */                0x05, 0x09,     /* Button */
+    /* Usage Minimum */             0x19, 0x01,     /* Button 1 */
+    /* Usage Maximum */             0x29, 0x03,     /* Button 3 */
+    /* Logical Minimum */           0x15, 0x00,     /* 0 */
+    /* Logical Maximum */           0x25, 0x01,     /* 1 */
+    /* Report Count */              0x95, 0x03,     /* 3 */
+    /* Report Size */               0x75, 0x01,     /* 1 */
+    /* Input */                     0x81, 0x02,     /* Data, Value, Absolute, Bit field */
+    /* Report Count */              0x95, 0x01,     /* 1 */
+    /* Report Size */               0x75, 0x05,     /* 5 (padding bits) */
+    /* Input */                     0x81, 0x03,     /* Constant, Value, Absolute, Bit field */
+    /* End Collection */            0xC0,
+    /* End Collection */            0xC0,
+};
+
 /* Additional HID class interface descriptor. */
-static const uint8_t g_UsbHidIfHidDesc[] =
+static const uint8_t g_UsbHidMIfHidDesc[] =
 {
     /* .bLength = */                0x09,
@@ -248,8 +323,20 @@
     /* .bNumDescriptors = */        1,
     /* .bDescriptorType = */        0x22,       /* Report */
-    /* .wDescriptorLength = */      sizeof(g_UsbHidReportDesc), 0x00
-};
-
-static const VUSBDESCINTERFACEEX g_UsbHidInterfaceDesc =
+    /* .wDescriptorLength = */      sizeof(g_UsbHidMReportDesc), 0x00
+};
+
+/* Additional HID class interface descriptor. */
+static const uint8_t g_UsbHidTIfHidDesc[] =
+{
+    /* .bLength = */                0x09,
+    /* .bDescriptorType = */        0x21,       /* HID */
+    /* .bcdHID = */                 0x10, 0x01, /* 1.1 */
+    /* .bCountryCode = */           0,
+    /* .bNumDescriptors = */        1,
+    /* .bDescriptorType = */        0x22,       /* Report */
+    /* .wDescriptorLength = */      sizeof(g_UsbHidTReportDesc), 0x00
+};
+
+static const VUSBDESCINTERFACEEX g_UsbHidMInterfaceDesc =
 {
     {
@@ -265,15 +352,39 @@
     },
     /* .pvMore = */     NULL,
-    /* .pvClass = */    &g_UsbHidIfHidDesc,
-    /* .cbClass = */    sizeof(g_UsbHidIfHidDesc),
-    &g_aUsbHidEndpointDescs[0]
-};
-
-static const VUSBINTERFACE g_aUsbHidInterfaces[] =
-{
-    { &g_UsbHidInterfaceDesc, /* .cSettings = */ 1 },
-};
-
-static const VUSBDESCCONFIGEX g_UsbHidConfigDesc =
+    /* .pvClass = */    &g_UsbHidMIfHidDesc,
+    /* .cbClass = */    sizeof(g_UsbHidMIfHidDesc),
+    &g_aUsbHidMEndpointDescs[0]
+};
+
+static const VUSBDESCINTERFACEEX g_UsbHidTInterfaceDesc =
+{
+    {
+        /* .bLength = */                sizeof(VUSBDESCINTERFACE),
+        /* .bDescriptorType = */        VUSB_DT_INTERFACE,
+        /* .bInterfaceNumber = */       0,
+        /* .bAlternateSetting = */      0,
+        /* .bNumEndpoints = */          1,
+        /* .bInterfaceClass = */        3 /* HID */,
+        /* .bInterfaceSubClass = */     1 /* Boot Interface */,
+        /* .bInterfaceProtocol = */     2 /* Mouse */,
+        /* .iInterface = */             0
+    },
+    /* .pvMore = */     NULL,
+    /* .pvClass = */    &g_UsbHidTIfHidDesc,
+    /* .cbClass = */    sizeof(g_UsbHidTIfHidDesc),
+    &g_aUsbHidTEndpointDescs[0]
+};
+
+static const VUSBINTERFACE g_aUsbHidMInterfaces[] =
+{
+    { &g_UsbHidMInterfaceDesc, /* .cSettings = */ 1 },
+};
+
+static const VUSBINTERFACE g_aUsbHidTInterfaces[] =
+{
+    { &g_UsbHidTInterfaceDesc, /* .cSettings = */ 1 },
+};
+
+static const VUSBDESCCONFIGEX g_UsbHidMConfigDesc =
 {
     {
@@ -281,5 +392,5 @@
         /* .bDescriptorType = */    VUSB_DT_CONFIG,
         /* .wTotalLength = */       0 /* recalculated on read */,
-        /* .bNumInterfaces = */     RT_ELEMENTS(g_aUsbHidInterfaces),
+        /* .bNumInterfaces = */     RT_ELEMENTS(g_aUsbHidMInterfaces),
         /* .bConfigurationValue =*/ 1,
         /* .iConfiguration = */     0,
@@ -288,10 +399,26 @@
     },
     NULL,
-    &g_aUsbHidInterfaces[0]
-};
-
-static const VUSBDESCDEVICE g_UsbHidDeviceDesc =
-{
-    /* .bLength = */                sizeof(g_UsbHidDeviceDesc),
+    &g_aUsbHidMInterfaces[0]
+};
+
+static const VUSBDESCCONFIGEX g_UsbHidTConfigDesc =
+{
+    {
+        /* .bLength = */            sizeof(VUSBDESCCONFIG),
+        /* .bDescriptorType = */    VUSB_DT_CONFIG,
+        /* .wTotalLength = */       0 /* recalculated on read */,
+        /* .bNumInterfaces = */     RT_ELEMENTS(g_aUsbHidTInterfaces),
+        /* .bConfigurationValue =*/ 1,
+        /* .iConfiguration = */     0,
+        /* .bmAttributes = */       RT_BIT(7),
+        /* .MaxPower = */           50 /* 100mA */
+    },
+    NULL,
+    &g_aUsbHidTInterfaces[0]
+};
+
+static const VUSBDESCDEVICE g_UsbHidMDeviceDesc =
+{
+    /* .bLength = */                sizeof(g_UsbHidMDeviceDesc),
     /* .bDescriptorType = */        VUSB_DT_DEVICE,
     /* .bcdUsb = */                 0x110,  /* 1.1 */
@@ -304,13 +431,41 @@
     /* .bcdDevice = */              0x0100, /* 1.0 */
     /* .iManufacturer = */          USBHID_STR_ID_MANUFACTURER,
-    /* .iProduct = */               USBHID_STR_ID_PRODUCT,
+    /* .iProduct = */               USBHID_STR_ID_PRODUCT_M,
     /* .iSerialNumber = */          0,
     /* .bNumConfigurations = */     1
 };
 
-static const PDMUSBDESCCACHE g_UsbHidDescCache =
-{
-    /* .pDevice = */                &g_UsbHidDeviceDesc,
-    /* .paConfigs = */              &g_UsbHidConfigDesc,
+static const VUSBDESCDEVICE g_UsbHidTDeviceDesc =
+{
+    /* .bLength = */                sizeof(g_UsbHidTDeviceDesc),
+    /* .bDescriptorType = */        VUSB_DT_DEVICE,
+    /* .bcdUsb = */                 0x110,  /* 1.1 */
+    /* .bDeviceClass = */           0 /* Class specified in the interface desc. */,
+    /* .bDeviceSubClass = */        0 /* Subclass specified in the interface desc. */,
+    /* .bDeviceProtocol = */        0 /* Protocol specified in the interface desc. */,
+    /* .bMaxPacketSize0 = */        8,
+    /* .idVendor = */               VBOX_USB_VENDOR,
+    /* .idProduct = */              USBHID_PID_TABLET,
+    /* .bcdDevice = */              0x0100, /* 1.0 */
+    /* .iManufacturer = */          USBHID_STR_ID_MANUFACTURER,
+    /* .iProduct = */               USBHID_STR_ID_PRODUCT_T,
+    /* .iSerialNumber = */          0,
+    /* .bNumConfigurations = */     1
+};
+
+static const PDMUSBDESCCACHE g_UsbHidMDescCache =
+{
+    /* .pDevice = */                &g_UsbHidMDeviceDesc,
+    /* .paConfigs = */              &g_UsbHidMConfigDesc,
+    /* .paLanguages = */            g_aUsbHidLanguages,
+    /* .cLanguages = */             RT_ELEMENTS(g_aUsbHidLanguages),
+    /* .fUseCachedDescriptors = */  true,
+    /* .fUseCachedStringsDescriptors = */ true
+};
+
+static const PDMUSBDESCCACHE g_UsbHidTDescCache =
+{
+    /* .pDevice = */                &g_UsbHidTDeviceDesc,
+    /* .paConfigs = */              &g_UsbHidTConfigDesc,
     /* .paLanguages = */            g_aUsbHidLanguages,
     /* .cLanguages = */             RT_ELEMENTS(g_aUsbHidLanguages),
@@ -542,5 +697,5 @@
 
 /**
- * Mouse event handler.
+ * Relative mouse event handler.
  *
  * @returns VBox status code.
@@ -558,4 +713,8 @@
 //    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);
+
     /* Accumulate movement - the events from the front end may arrive
      * at a much higher rate than USB can handle.
@@ -571,6 +730,6 @@
     if (pUrb)
     {
+        size_t          cbCopy;
         USBHIDM_REPORT  report;
-        size_t          cbCopy;
 
         //@todo: fix/extend
@@ -582,8 +741,65 @@
         cbCopy = sizeof(report);
         memcpy(&pUrb->abData[0], &report, cbCopy);
-        usbHidCompleteOk(pThis, pUrb, 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);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Absolute mouse event handler.
+ *
+ * @returns VBox status code.
+ * @param   pInterface      Pointer to the mouse port interface (KBDState::Mouse.iPort).
+ * @param   u32X            The X coordinate.
+ * @param   u32Y            The Y coordinate.
+ * @param   i32DeltaZ       The Z delta.
+ * @param   i32DeltaW       The W delta.
+ * @param   fButtonStates   The button states.
+ */
+static DECLCALLBACK(int) usbHidMousePutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t u32X, uint32_t u32Y, int32_t i32DeltaZ, int32_t i32DeltaW, uint32_t fButtonStates)
+{
+    PUSBHID pThis = RT_FROM_MEMBER(pInterface, USBHID, Lun0.IPort);
+//    int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
+//    AssertReleaseRC(rc);
+
+    Assert(pThis->isAbsolute);
+
+    /* 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.
+     */
+    pThis->PtrDelta.btn = fButtonStates;
+    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 / 2;
+        report.cy  = u32Y / 2;
+        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));
     }
 
@@ -734,11 +950,23 @@
                         {
                             case DT_IF_HID_REPORT:
-                                uint32_t    cbCopy;
-
+                                uint32_t        cbCopy;
+                                uint32_t        cbDesc;
+                                const uint8_t   *pDesc;
+
+                                if (pThis->isAbsolute)
+                                {
+                                    cbDesc = sizeof(g_UsbHidTReportDesc);
+                                    pDesc = (const uint8_t *)&g_UsbHidTReportDesc;
+                                }
+                                else
+                                {
+                                    cbDesc = sizeof(g_UsbHidMReportDesc);
+                                    pDesc = (const uint8_t *)&g_UsbHidMReportDesc;
+                                }
                                 /* Returned data is written after the setup message. */
                                 cbCopy = pUrb->cbData - sizeof(*pSetup);
-                                cbCopy = RT_MIN(cbCopy, sizeof(g_UsbHidReportDesc));
+                                cbCopy = RT_MIN(cbCopy, cbDesc);
                                 Log(("usbHid: GET_DESCRIPTOR DT_IF_HID_REPORT wValue=%#x wIndex=%#x cbCopy=%#x\n", pSetup->wValue, pSetup->wIndex, cbCopy));
-                                memcpy(&pUrb->abData[sizeof(*pSetup)], &g_UsbHidReportDesc, cbCopy);
+                                memcpy(&pUrb->abData[sizeof(*pSetup)], pDesc, cbCopy);
                                 return usbHidCompleteOk(pThis, pUrb, cbCopy + sizeof(*pSetup));
                             default:
@@ -871,4 +1099,9 @@
     pThis->bConfigurationValue = bConfigurationValue;
 
+    /* 
+     * Set received event type to absolute or relative.
+     */
+    pThis->Lun0.pDrv->pfnAbsModeChange(pThis->Lun0.pDrv, pThis->isAbsolute);
+
     RTCritSectLeave(&pThis->CritSect);
     return VINF_SUCCESS;
@@ -883,5 +1116,9 @@
     PUSBHID pThis = PDMINS_2_DATA(pUsbIns, PUSBHID);
     LogFlow(("usbHidUsbGetDescriptorCache/#%u:\n", pUsbIns->iInstance));
-    return &g_UsbHidDescCache;
+    if (pThis->isAbsolute) {
+        return &g_UsbHidTDescCache;
+    } else {
+        return &g_UsbHidMDescCache;
+    }
 }
 
@@ -952,10 +1189,14 @@
      * Validate and read the configuration.
      */
-    rc = CFGMR3ValidateConfig(pCfg, "/", "", "", "UsbHid", iInstance);
+    rc = CFGMR3ValidateConfig(pCfg, "/", "Absolute", "Config", "UsbHid", iInstance);
     if (RT_FAILURE(rc))
         return rc;
-
+    rc = CFGMR3QueryBoolDef(pCfg, "Absolute", &pThis->isAbsolute, false);
+    if (RT_FAILURE(rc))
+        return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS, N_("HID failed to query settings"));
+    
     pThis->Lun0.IBase.pfnQueryInterface = usbHidMouseQueryInterface;
     pThis->Lun0.IPort.pfnPutEvent       = usbHidMousePutEvent;
+    pThis->Lun0.IPort.pfnPutEventAbs    = usbHidMousePutEventAbs;
 
     /*
@@ -965,4 +1206,8 @@
     if (RT_FAILURE(rc))
         return PDMUsbHlpVMSetError(pUsbIns, rc, RT_SRC_POS, N_("HID failed to attach mouse driver"));
+
+    pThis->Lun0.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Lun0.pDrvBase, PDMIMOUSECONNECTOR);
+    if (!pThis->Lun0.pDrv)
+        return PDMUsbHlpVMSetError(pUsbIns, VERR_PDM_MISSING_INTERFACE, RT_SRC_POS, N_("HID failed to query mouse interface"));
 
     return VINF_SUCCESS;
