Index: /trunk/include/VBox/pdmifs.h
===================================================================
--- /trunk/include/VBox/pdmifs.h	(revision 26623)
+++ /trunk/include/VBox/pdmifs.h	(revision 26624)
@@ -304,4 +304,16 @@
      */
     DECLR3CALLBACKMEMBER(int, pfnPutEvent,(PPDMIMOUSEPORT pInterface, int32_t i32DeltaX, int32_t i32DeltaY, int32_t i32DeltaZ, int32_t i32DeltaW, uint32_t fButtonStates));
+    /**
+     * Puts an absolute mouse event.
+     * This is called by the source of mouse events. The event will be passed up until the
+     * topmost driver, which then calls the registered event handler.
+     *
+     * @returns VBox status code.
+     * @param   pInterface          Pointer to this interface structure.
+     * @param   i32cX               The X value, in the range 0 to 0xffff.
+     * @param   i32cY               The Y value, in the range 0 to 0xffff.
+     * @thread  The emulation thread.
+     */
+    DECLR3CALLBACKMEMBER(int, pfnPutEventAbs,(PPDMIMOUSEPORT pInterface, int32_t i32cX, int32_t i32cY));
 } PDMIMOUSEPORT;
 
@@ -316,11 +328,22 @@
 
 
+/** Pointer to a mouse connector interface. */
+typedef struct PDMIMOUSECONNECTOR *PPDMIMOUSECONNECTOR;
 /**
  * Mouse connector interface (up).
  * Pair with PDMIMOUSEPORT.
  */
-typedef PDMIDUMMY PDMIMOUSECONNECTOR;
- /** Pointer to a mouse connector interface. */
-typedef PDMIMOUSECONNECTOR *PPDMIMOUSECONNECTOR;
+typedef struct PDMIMOUSECONNECTOR
+{
+    /**
+     * Notifies the the downstream driver when the guest switches the device into or out of absolute mode.
+     *
+     * @param   pInterface      Pointer to the this interface.
+     * @param   fAbs            Whether absolute mode is currently enabled
+     */
+    DECLR3CALLBACKMEMBER(void, pfnAbsModeChange,(PPDMIMOUSECONNECTOR pInterface, bool fAbs));
+
+} PDMIMOUSECONNECTOR;
+
 /** PDMIMOUSECONNECTOR interface ID.  */
 #define PDMIMOUSECONNECTOR_IID                  "847f965f-0eb8-4363-88ac-b0ee58a05bde"
Index: /trunk/src/VBox/Devices/Input/DevPS2.cpp
===================================================================
--- /trunk/src/VBox/Devices/Input/DevPS2.cpp	(revision 26623)
+++ /trunk/src/VBox/Devices/Input/DevPS2.cpp	(revision 26624)
@@ -57,5 +57,5 @@
 #include "../Builtins.h"
 
-#define PCKBD_SAVED_STATE_VERSION 3
+#define PCKBD_SAVED_STATE_VERSION 4
 
 
@@ -162,6 +162,26 @@
 #define KBD_QUEUE_SIZE 256
 
+/* Supported mouse protocols */
+enum
+{
+    MOUSE_PROT_PS2 = 0,
+    MOUSE_PROT_IMPS2 = 3,
+    MOUSE_PROT_IMEX = 4,
+    MOUSE_PROT_LIFEBOOK = 5
+};
+
+/* Mouse flags */
 # define MOUSE_REPORT_HORIZONTAL  0x01
-# define MOUSE_OUTSTANDING_CLICK  0x02
+
+/** Extended mouse button values for Lifebook mode */
+/** Downwards scrollwheel movement of one step.  Doesn't affect the mouse
+ * buttons */
+# define MOUSE_EXT_VSCROLL_DN   4
+/** Upwards scrollwheel movement of one step. */
+# define MOUSE_EXT_VSCROLL_UP   5
+/** Leftwards scrollwheel movement of one step. */
+# define MOUSE_EXT_HSCROLL_BW   6
+/** Rightwards scrollwheel movement of one step. */
+# define MOUSE_EXT_HSCROLL_FW   7
 
 typedef struct {
@@ -210,5 +230,9 @@
     int32_t mouse_dw;
     int32_t mouse_flags;
+    int32_t mouse_cx;
+    int32_t mouse_cy;
     uint8_t mouse_buttons;
+    uint8_t mouse_buttons_reported;
+    uint8_t mouse_last_button;
 
     /** Pointer to the device instance - RC. */
@@ -647,74 +671,236 @@
 }
 
-static void kbd_mouse_send_packet(KBDState *s, bool fToCmdQueue)
+static void kbd_mouse_set_reported_buttons(KBDState *s, unsigned fButtons, unsigned fButtonMask)
+{
+    s->mouse_buttons_reported |= (fButtons & fButtonMask);
+    s->mouse_buttons_reported &= (fButtons | ~fButtonMask);
+}
+
+static bool kbd_mouse_test_set_button(KBDState *s, unsigned cIndex)
+{
+    unsigned fButtonMask = 1 << (cIndex - 1);
+
+    AssertReturn(3 <= cIndex && cIndex <= 5, false);
+    if (   (s->mouse_buttons & fButtonMask)
+        && !(s->mouse_buttons_reported & fButtonMask))
+    {
+        s->mouse_last_button = cIndex;
+        kbd_mouse_set_reported_buttons(s, fButtonMask, 0x1c);
+        return true;
+    }
+    return false;
+}
+
+static bool kbd_mouse_test_clear_last_button(KBDState *s)
+{
+    unsigned fButtonMask = 1 << (s->mouse_last_button - 1);
+
+    if (   s->mouse_last_button != 0
+        && !(s->mouse_buttons & fButtonMask))
+    {
+        s->mouse_last_button = 0;
+        kbd_mouse_set_reported_buttons(s, 0, fButtonMask);
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Send a single relative packet in 3-byte PS/2 format, optionally with our 
+ * packed button protocol extension, to the PS/2 controller.
+ * @param  s               keyboard state object
+ * @param  dx              relative X value, must be between -256 and +255
+ * @param  dy              relative y value, must be between -256 and +255
+ * @param  fButtonsLow     the state of the two first mouse buttons
+ * @param  fButtonsPacked  the state of the upper three mouse buttons and
+ *                         scroll wheel movement, packed as per the
+ *                         MOUSE_EXT_* defines.  For standard PS/2 packets
+ *                         only pass the value of button 3 here.
+ */
+static void kbd_mouse_send_rel3_packet(KBDState *s, bool fToCmdQueue)
 {
     int aux = fToCmdQueue ? 1 : 2;
+    int dx1 =   s->mouse_dx < 0 ? RT_MAX(s->mouse_dx, -256)
+              : s->mouse_dx > 0 ? RT_MIN(s->mouse_dx, 255) : 0;
+    int dy1 =   s->mouse_dy < 0 ? RT_MAX(s->mouse_dy, -256)
+              : s->mouse_dy > 0 ? RT_MIN(s->mouse_dy, 255) : 0;
     unsigned int b;
-    int dx1, dy1, dz1, dw1;
-
-    dx1 = s->mouse_dx;
-    dy1 = s->mouse_dy;
-    dz1 = s->mouse_dz;
-    dw1 = s->mouse_dw;
-    LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d\n", __PRETTY_FUNCTION__,
-             dx1, dy1, dz1, dw1));
-    /* XXX: increase range to 8 bits ? */
-    if (dx1 > 127)
-        dx1 = 127;
-    else if (dx1 < -127)
-        dx1 = -127;
-    if (dy1 > 127)
-        dy1 = 127;
-    else if (dy1 < -127)
-        dy1 = -127;
-    b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
+    unsigned fButtonsPacked;
+    unsigned fButtonsLow = s->mouse_buttons & 0x03;
+    s->mouse_dx -= dx1;
+    s->mouse_dy -= dy1;
+    kbd_mouse_set_reported_buttons(s, fButtonsLow, 0x03);
+    /* When we are not in lifebook mode, we just set the third bit
+     * in the first packet byte if the middle button is pressed,
+     * as per the PS/2 protocol. */
+    if (s->mouse_type != MOUSE_PROT_LIFEBOOK)
+    {
+        fButtonsPacked = (s->mouse_buttons & 0x04 ? 0x04 : 0);
+        kbd_mouse_set_reported_buttons(s, s->mouse_buttons, 0x04);
+    }
+    else
+    {
+        if (kbd_mouse_test_set_button(s, 3))
+            fButtonsPacked = 1;
+        else if (kbd_mouse_test_set_button(s, 4))
+            fButtonsPacked = 2;
+        else if (kbd_mouse_test_set_button(s, 5))
+            fButtonsPacked = 3;
+        /* Release event for buttons in the range 3-5. */
+        else if (kbd_mouse_test_clear_last_button(s))
+            fButtonsPacked = 0;
+        else if (s->mouse_dz < 0)
+        {
+            ++s->mouse_dz;
+            fButtonsPacked = MOUSE_EXT_VSCROLL_DN;
+        }
+        else if (s->mouse_dz > 0)
+        {
+            --s->mouse_dz;
+            fButtonsPacked = MOUSE_EXT_VSCROLL_UP;
+        }
+        else if (s->mouse_dw < 0)
+        {
+            ++s->mouse_dw;
+            fButtonsPacked = MOUSE_EXT_HSCROLL_BW;
+        }
+        else if (s->mouse_dw > 0)
+        {
+            --s->mouse_dw;
+            fButtonsPacked = MOUSE_EXT_HSCROLL_FW;
+        }
+        else
+            fButtonsPacked = s->mouse_last_button;
+    }
+    LogRel3(("%s: dx1=%d, dy1=%d, fButtonsLow=0x%x, fButtonsPacked=0x%x\n",
+             __PRETTY_FUNCTION__, dx1, dy1, fButtonsLow, fButtonsPacked));
+    b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | fButtonsLow
+             | (fButtonsPacked & 4) | ((fButtonsPacked & 3) << 6);
     kbd_queue(s, b, aux);
     kbd_queue(s, dx1 & 0xff, aux);
     kbd_queue(s, dy1 & 0xff, aux);
-    /* extra byte for IMPS/2 or IMEX */
-    switch(s->mouse_type) {
-    default:
-        break;
-    case 3:
-        if (dz1 > 127)
-            dz1 = 127;
-        else if (dz1 < -127)
-                dz1 = -127;
-        kbd_queue(s, dz1 & 0xff, aux);
-        break;
-    case 4:
-        if (dz1 > 1)
-            dz1 = 1;
-        else if (dz1 < -1)
-            dz1 = -1;
-        else if (dw1 > 1)
-            dw1 = 1;
-        else if (dw1 < -1)
-            dw1 = -1;
-        if (dz1)
-            dw1 = 0;
-        if ((s->mouse_flags & MOUSE_REPORT_HORIZONTAL) && dw1)
-            b = 0x40 | (dw1 & 0x3f);
-        else
-        {
-            b =   (dz1 & 0x0f) | ((dw1 << 1) & 0x0f)
-                | ((s->mouse_buttons & 0x18) << 1);
-            s->mouse_flags &= ~MOUSE_OUTSTANDING_CLICK;
-        }
-        kbd_queue(s, b, aux);
-        break;
-    }
-
-    /* update deltas */
-    s->mouse_dx -= dx1;
-    s->mouse_dy -= dy1;
+}
+
+static void kbd_mouse_send_imps2_byte4(KBDState *s, bool fToCmdQueue)
+{
+    int aux = fToCmdQueue ? 1 : 2;
+
+    int dz1 =   s->mouse_dz < 0 ? RT_MAX(s->mouse_dz, -127)
+              : s->mouse_dz > 0 ? RT_MIN(s->mouse_dz, 127) : 0;
     s->mouse_dz -= dz1;
-    s->mouse_dw -= dw1;
+    kbd_queue(s, dz1 & 0xff, aux);
+}
+
+static void kbd_mouse_send_imex_byte4(KBDState *s, bool fToCmdQueue)
+{
+    int aux = fToCmdQueue ? 1 : 2;
+
+    if (s->mouse_dw)
+    {
+        int dw1 =   s->mouse_dw < 0 ? RT_MAX(s->mouse_dw, -32)
+                  : s->mouse_dw > 0 ? RT_MIN(s->mouse_dw, 32) : 0;
+        s->mouse_dw -= dw1;
+        kbd_queue(s, 0x40 | (dw1 & 0x3f), aux);
+    }
+    else if (s->mouse_flags & MOUSE_REPORT_HORIZONTAL && s->mouse_dz)
+    {
+        int dz1 =   s->mouse_dz < 0 ? RT_MAX(s->mouse_dz, -32)
+                  : s->mouse_dz > 0 ? RT_MIN(s->mouse_dz, 32) : 0;
+        s->mouse_dz -= dz1;
+        kbd_queue(s, 0x80 | (dz1 & 0x3f), aux);
+    }
+    else
+    {
+        int dz1 =   s->mouse_dz < 0 ? RT_MAX(s->mouse_dz, -8)
+                  : s->mouse_dz > 0 ? RT_MIN(s->mouse_dz, 8) : 0;
+        s->mouse_dz -= dz1;
+        kbd_mouse_set_reported_buttons(s, s->mouse_buttons, 0x18);
+        kbd_queue(s, (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1), aux);
+    }
+}
+
+/**
+ * Send a single relative packet in (IM)PS/2 or IMEX format to the PS/2
+ * controller.
+ * @param  s            keyboard state object
+ * @param  fToCmdQueue  should this packet go to the command queue (or the
+ *                      event queue)?
+ */
+static void kbd_mouse_send_rel_packet(KBDState *s, bool fToCmdQueue)
+{
+    kbd_mouse_send_rel3_packet(s, fToCmdQueue);
+    if (s->mouse_type == MOUSE_PROT_IMPS2)
+        kbd_mouse_send_imps2_byte4(s, fToCmdQueue);
+    if (s->mouse_type == MOUSE_PROT_IMEX)
+        kbd_mouse_send_imex_byte4(s, fToCmdQueue);
+}
+
+/**
+ * Send a single absolute packet in 6-byte lifebook format to the PS/2
+ * controller.
+ * @param  s         keyboard state object
+ * @param  cx        absolute X value
+ * @param  cy        absolute y value
+ * @param  fButtons  the state of the two first mouse buttons
+ */
+static void kbd_mouse_send_abs_packet(KBDState *s, bool fToCmdQueue)
+{
+    int aux = fToCmdQueue ? 1 : 2;
+    int cx1 = s->mouse_cx * 4096 / 0xffff;
+    int cy1 = 4096 - (s->mouse_cy * 4096 / 0xffff);
+    unsigned fButtons = s->mouse_buttons & 0x03;
+    unsigned int b;
+
+    LogRel3(("%s: cx1=%d, cy1=%d, fButtons=0x%x\n", __PRETTY_FUNCTION__,
+             cx1, cy1, fButtons));
+    b = 4 /* Screen is being touched */ | fButtons;
+    kbd_queue(s, b, aux);
+    b = ((cy1 << 2) & 0xc0) | (cx1 >> 6);
+    kbd_queue(s, b, aux);
+    b = ((cx1 << 2) & 0xc0) | (cx1 & 0x3f);
+    kbd_queue(s, b, aux);
+    kbd_queue(s, 0xc0, aux);  /* This byte is really wasted in the protocol */
+    b = ((cx1 << 2) & 0xc0) | (cy1 >> 6);
+    kbd_queue(s, b, aux);
+    b = ((cy1 << 2) & 0xc0) | (cy1 & 0x3f);
+    kbd_queue(s, b, aux);
+}
+
+static bool kbd_mouse_rel_unreported(KBDState *s)
+{
+   return    s->mouse_dx
+          || s->mouse_dy
+          || s->mouse_dz
+          || s->mouse_dw
+          || s->mouse_buttons != s->mouse_buttons_reported;
+}
+
+/**
+ * Send a single packet in (IM)PS/2, IMEX or Lifebook format to the PS/2
+ * controller.
+ * @param  s            keyboard state object
+ * @param  fToCmdQueue  is this the result of a poll on the mouse controller?
+ */
+static void kbd_mouse_send_packet(KBDState *s, bool fToCmdQueue)
+{
+    if (   kbd_mouse_rel_unreported(s)
+        || (s->mouse_type != MOUSE_PROT_LIFEBOOK))
+        kbd_mouse_send_rel_packet(s, fToCmdQueue);
+    else
+        kbd_mouse_send_abs_packet(s, fToCmdQueue);
 }
 
 #ifdef IN_RING3
-static void pc_kbd_mouse_event(void *opaque,
-                               int dx, int dy, int dz, int dw, int buttons_state)
-{
+static size_t kbd_mouse_event_queue_free(KBDState *s)
+{
+    AssertReturn(s->mouse_event_queue.count <= MOUSE_EVENT_QUEUE_SIZE, 0);
+    return MOUSE_EVENT_QUEUE_SIZE - s->mouse_event_queue.count;
+}
+
+static void pc_kbd_mouse_event(void *opaque, int dx, int dy, int dz, int dw,
+                               int buttons_state)
+{
+    LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d, buttons_state=0x%x\n",
+             __PRETTY_FUNCTION__, dx, dy, dz, dw, buttons_state));
     KBDState *s = (KBDState*)opaque;
 
@@ -722,31 +908,44 @@
     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
         return;
+    AssertReturnVoid((buttons_state & ~0x1f) == 0);
 
     s->mouse_dx += dx;
     s->mouse_dy -= dy;
     s->mouse_dz += dz;
-    s->mouse_dw += dw;
-    /* In horizontal reporting mode, we may need to send an additional packet
-     * for the forth and fifth buttons, as they can't share a packet with a
-     * horizontal scroll delta. */
-    if (   s->mouse_type == 4
-        && (s->mouse_buttons & 0x18) != (buttons_state & 0x18))
-        s->mouse_flags |= MOUSE_OUTSTANDING_CLICK;
+    if (   (   (s->mouse_type == MOUSE_PROT_IMEX)
+            && s->mouse_flags & MOUSE_REPORT_HORIZONTAL)
+        || (s->mouse_type == MOUSE_PROT_LIFEBOOK))
+        s->mouse_dw += dw;
     s->mouse_buttons = buttons_state;
+    if (!(s->mouse_status & MOUSE_STATUS_REMOTE))
+        /* if not remote, send event. Multiple events are sent if
+           too big deltas */
+        while (   kbd_mouse_rel_unreported(s)
+               && kbd_mouse_event_queue_free(s) > 4)
+            kbd_mouse_send_rel_packet(s, false);
+}
+
+static void pc_kbd_mouse_event_abs(void *opaque, unsigned cx, unsigned cy)
+{
+    LogRel3(("%s: cx=%d, cy=%d\n", __PRETTY_FUNCTION__, cx, cy));
+    KBDState *s = (KBDState*)opaque;
+
+    if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
+        return;
+
+    if (s->mouse_type != MOUSE_PROT_LIFEBOOK)
+        return;
+
+    s->mouse_cx = cx;
+    s->mouse_cy = cy;
 
     if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
-        (s->mouse_event_queue.count < (MOUSE_EVENT_QUEUE_SIZE - 4))) {
-        for(;;) {
-            /* if not remote, send event. Multiple events are sent if
-               too big deltas */
-            kbd_mouse_send_packet(s, false);
-            if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 && s->mouse_dw == 0 && !(s->mouse_flags & MOUSE_OUTSTANDING_CLICK))
-                break;
-        }
-    }
+        (s->mouse_event_queue.count < (MOUSE_EVENT_QUEUE_SIZE - 4)))
+        /* if not remote, send event */
+        kbd_mouse_send_abs_packet(s, false);
 }
 #endif /* IN_RING3 */
 
-static void kbd_write_mouse(KBDState *s, int val)
+static int kbd_write_mouse(KBDState *s, int val)
 {
 #ifdef DEBUG_MOUSE
@@ -765,8 +964,8 @@
                 s->mouse_wrap = 0;
                 kbd_queue(s, AUX_ACK, 1);
-                return;
+                return VINF_SUCCESS;
             } else if (val != AUX_RESET) {
                 kbd_queue(s, val, 1);
-                return;
+                return VINF_SUCCESS;
             }
         }
@@ -833,5 +1032,5 @@
             s->mouse_resolution = 2;
             s->mouse_status = 0;
-            s->mouse_type = 0;
+            s->mouse_type = MOUSE_PROT_PS2;
             kbd_queue(s, AUX_ACK, 1);
             kbd_queue(s, 0xaa, 1);
@@ -879,5 +1078,5 @@
             else if (val == 200)
                 s->mouse_detect_state = 3;
-            else if ((val == 80) && s->mouse_type == 4 /* IMEX */)
+            else if ((val == 80) && s->mouse_type == MOUSE_PROT_IMEX)
                 /* enable horizontal scrolling, byte two */
                 s->mouse_detect_state = 4;
@@ -889,5 +1088,5 @@
             {
                 LogRelFlowFunc(("switching mouse device to IMPS/2 mode\n"));
-                s->mouse_type = 3; /* IMPS/2 */
+                s->mouse_type = MOUSE_PROT_IMPS2;
             }
             s->mouse_detect_state = 0;
@@ -897,5 +1096,5 @@
             {
                 LogRelFlowFunc(("switching mouse device to IMEX mode\n"));
-                s->mouse_type = 4; /* IMEX */
+                s->mouse_type = MOUSE_PROT_IMEX;
             }
             s->mouse_detect_state = 0;
@@ -914,9 +1113,37 @@
         break;
     case AUX_SET_RES:
-        s->mouse_resolution = val;
-        kbd_queue(s, AUX_ACK, 1);
+        if (0 <= val && val < 4)
+        {
+            s->mouse_resolution = val;
+            kbd_queue(s, AUX_ACK, 1);
+        }
+        else if (val == 6)  /* Lifebook off magic knock */
+        {
+#ifdef IN_RING3
+            LogRelFlowFunc(("switching mouse device to basic PS/2 mode\n"));
+            s->mouse_type = MOUSE_PROT_PS2;
+            s->Mouse.pDrv->pfnAbsModeChange(s->Mouse.pDrv, false);
+#else
+            return VINF_IOM_HC_IOPORT_WRITE;
+#endif
+            kbd_queue(s, AUX_NACK, 1);
+        }
+        else if (val == 8)  /* Lifebook on magic knock */
+        {
+#ifdef IN_RING3
+            LogRelFlowFunc(("switching mouse device to touch screen mode\n"));
+            s->mouse_type = MOUSE_PROT_LIFEBOOK;
+            s->Mouse.pDrv->pfnAbsModeChange(s->Mouse.pDrv, true);
+#else
+            return VINF_IOM_HC_IOPORT_WRITE;
+#endif
+            kbd_queue(s, AUX_NACK, 1);
+        }
+        else
+            kbd_queue(s, AUX_NACK, 1);
         s->mouse_write_cmd = -1;
         break;
     }
+    return VINF_SUCCESS;
 }
 
@@ -963,5 +1190,5 @@
         break;
     case KBD_CCMD_WRITE_MOUSE:
-        kbd_write_mouse(s, val);
+        rc = kbd_write_mouse(s, val);
         break;
     default:
@@ -994,5 +1221,6 @@
     s->mouse_sample_rate = 0;
     s->mouse_wrap = 0;
-    s->mouse_type = 0;
+    s->mouse_type = MOUSE_PROT_PS2;
+    s->Mouse.pDrv->pfnAbsModeChange(s->Mouse.pDrv, false);
     s->mouse_detect_state = 0;
     s->mouse_dx = 0;
@@ -1001,5 +1229,9 @@
     s->mouse_dw = 0;
     s->mouse_flags = 0;
+    s->mouse_cx = 0x8000;
+    s->mouse_cy = 0x8000;
     s->mouse_buttons = 0;
+    s->mouse_buttons_reported = 0;
+    s->mouse_last_button = 0;
     q = &s->queue;
     q->rptr = 0;
@@ -1039,5 +1271,9 @@
     qemu_put_be32s(f, &s->mouse_dw);
     qemu_put_be32s(f, &s->mouse_flags);
+    qemu_put_be32s(f, &s->mouse_cx);
+    qemu_put_be32s(f, &s->mouse_cy);
     qemu_put_8s(f, &s->mouse_buttons);
+    qemu_put_8s(f, &s->mouse_buttons_reported);
+    qemu_put_8s(f, &s->mouse_last_button);
 
     /* XXX: s->scancode_set isn't being saved, but we only really support set 2,
@@ -1089,4 +1325,6 @@
     qemu_get_8s(f, &s->mouse_wrap);
     qemu_get_8s(f, &s->mouse_type);
+    if (s->mouse_type == MOUSE_PROT_LIFEBOOK)
+        s->Mouse.pDrv->pfnAbsModeChange(s->Mouse.pDrv, true);
     qemu_get_8s(f, &s->mouse_detect_state);
     qemu_get_be32s(f, (uint32_t *)&s->mouse_dx);
@@ -1099,4 +1337,11 @@
     }
     qemu_get_8s(f, &s->mouse_buttons);
+    if (version_id > 3)
+    {
+        SSMR3GetS32(f, &s->mouse_cx);
+        SSMR3GetS32(f, &s->mouse_cy);
+        SSMR3GetU8(f, &s->mouse_buttons_reported);
+        SSMR3GetU8(f, &s->mouse_last_button);
+    }
     s->queue.count = 0;
     s->queue.rptr = 0;
@@ -1423,4 +1668,24 @@
 
     pc_kbd_mouse_event(pThis, i32DeltaX, i32DeltaY, i32DeltaZ, i32DeltaW, fButtonStates);
+
+    PDMCritSectLeave(&pThis->CritSect);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Mouse event handler.
+ *
+ * @returns VBox status code.
+ * @param   pInterface      Pointer to the mouse port interface (KBDState::Mouse.IPort).
+ * @param   i32cX           The X value.
+ * @param   i32cY           The Y value.
+ */
+static DECLCALLBACK(int) kbdMousePutEventAbs(PPDMIMOUSEPORT pInterface, int32_t i32cX, int32_t i32cY)
+{
+    KBDState *pThis = RT_FROM_MEMBER(pInterface, KBDState, Mouse.IPort);
+    int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
+    AssertReleaseRC(rc);
+
+    pc_kbd_mouse_event_abs(pThis, i32cX, i32cY);
 
     PDMCritSectLeave(&pThis->CritSect);
@@ -1619,4 +1884,5 @@
     pThis->Mouse.IBase.pfnQueryInterface    = kbdMouseQueryInterface;
     pThis->Mouse.IPort.pfnPutEvent          = kbdMousePutEvent;
+    pThis->Mouse.IPort.pfnPutEventAbs       = kbdMousePutEventAbs;
 
     /*
Index: /trunk/src/VBox/Devices/Input/DrvMouseQueue.cpp
===================================================================
--- /trunk/src/VBox/Devices/Input/DrvMouseQueue.cpp	(revision 26623)
+++ /trunk/src/VBox/Devices/Input/DrvMouseQueue.cpp	(revision 26624)
@@ -69,4 +69,5 @@
     /** The core part owned by the queue manager. */
     PDMQUEUEITEMCORE    Core;
+    uint32_t            fAbs;
     int32_t             i32DeltaX;
     int32_t             i32DeltaY;
@@ -74,4 +75,6 @@
     int32_t             i32DeltaW;
     uint32_t            fButtonStates;
+    int32_t             i32cX;
+    int32_t             i32cY;
 } DRVMOUSEQUEUEITEM, *PDRVMOUSEQUEUEITEM;
 
@@ -121,4 +124,5 @@
     if (pItem)
     {
+        pItem->fAbs = 0;
         pItem->i32DeltaX = i32DeltaX;
         pItem->i32DeltaY = i32DeltaY;
@@ -132,4 +136,52 @@
 }
 
+/**
+ * Queues an absolute mouse event.
+ * Because of the event queueing the EMT context requirement is lifted.
+ *
+ * @returns VBox status code.
+ * @param   pInterface      Pointer to interface structure.
+ * @param   i32cX           The X value.
+ * @param   i32cY           The Y value.
+ * @thread  Any thread.
+ */
+static DECLCALLBACK(int) drvMouseQueuePutEventAbs(PPDMIMOUSEPORT pInterface, int32_t i32cX, int32_t i32cY)
+{
+    PDRVMOUSEQUEUE pDrv = IMOUSEPORT_2_DRVMOUSEQUEUE(pInterface);
+    if (pDrv->fInactive)
+        return VINF_SUCCESS;
+
+    PDRVMOUSEQUEUEITEM pItem = (PDRVMOUSEQUEUEITEM)PDMQueueAlloc(pDrv->pQueue);
+    if (pItem)
+    {
+        pItem->fAbs = 1;
+        pItem->i32cX = i32cX;
+        pItem->i32cY = i32cY;
+        PDMQueueInsert(pDrv->pQueue, &pItem->Core);
+        return VINF_SUCCESS;
+    }
+    return VERR_PDM_NO_QUEUE_ITEMS;
+}
+
+
+/* -=-=-=-=- IConnector -=-=-=-=- */
+
+#define PPDMIMOUSECONNECTOR_2_DRVMOUSEQUEUE(pInterface) ( (PDRVMOUSEQUEUE)((char *)(pInterface) - RT_OFFSETOF(DRVMOUSEQUEUE, IConnector)) )
+
+
+/**
+ * Pass absolute mode status changes from the guest through to the frontend
+ * driver.
+ *
+ * @param   pInterface  Pointer to the mouse connector interface structure.
+ * @param   fAbs        The new absolute mode state.
+ */
+static DECLCALLBACK(void) drvMousePassThruAbsMode(PPDMIMOUSECONNECTOR pInterface, bool fAbs)
+{
+    PDRVMOUSEQUEUE pDrv = PPDMIMOUSECONNECTOR_2_DRVMOUSEQUEUE(pInterface);
+    pDrv->pDownConnector->pfnAbsModeChange(pDrv->pDownConnector, fAbs);
+}
+
+
 
 /* -=-=-=-=- queue -=-=-=-=- */
@@ -147,5 +199,9 @@
     PDRVMOUSEQUEUE        pThis = PDMINS_2_DATA(pDrvIns, PDRVMOUSEQUEUE);
     PDRVMOUSEQUEUEITEM    pItem = (PDRVMOUSEQUEUEITEM)pItemCore;
-    int rc = pThis->pUpPort->pfnPutEvent(pThis->pUpPort, pItem->i32DeltaX, pItem->i32DeltaY, pItem->i32DeltaZ, pItem->i32DeltaW, pItem->fButtonStates);
+    int rc;
+    if (!pItem->fAbs)
+        rc = pThis->pUpPort->pfnPutEvent(pThis->pUpPort, pItem->i32DeltaX, pItem->i32DeltaY, pItem->i32DeltaZ, pItem->i32DeltaW, pItem->fButtonStates);
+    else
+        rc = pThis->pUpPort->pfnPutEventAbs(pThis->pUpPort, pItem->i32cX, pItem->i32cY);
     return RT_SUCCESS(rc);
 }
@@ -241,6 +297,9 @@
     /* IBase. */
     pDrvIns->IBase.pfnQueryInterface        = drvMouseQueueQueryInterface;
+    /* IMouseConnector. */
+    pDrv->IConnector.pfnAbsModeChange       = drvMousePassThruAbsMode;
     /* IMousePort. */
     pDrv->IPort.pfnPutEvent                 = drvMouseQueuePutEvent;
+    pDrv->IPort.pfnPutEventAbs              = drvMouseQueuePutEventAbs;
 
     /*
Index: /trunk/src/VBox/Main/MouseImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/MouseImpl.cpp	(revision 26623)
+++ /trunk/src/VBox/Main/MouseImpl.cpp	(revision 26624)
@@ -46,5 +46,6 @@
 } DRVMAINMOUSE, *PDRVMAINMOUSE;
 
-
+/** Converts a PDMIMOUSECONNECTOR pointer to a DRVMAINMOUSE pointer. */
+#define PPDMIMOUSECONNECTOR_2_MAINMOUSE(pInterface) ( (PDRVMAINMOUSE) ((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINMOUSE, IConnector)) )
 
 // constructor / destructor
@@ -56,6 +57,7 @@
 {
     mpDrv = NULL;
-    mLastAbsX = 0;
-    mLastAbsY = 0;
+    mLastAbsX = 0x8000;
+    mLastAbsY = 0x8000;
+    mLastButtons = 0;
     return S_OK;
 }
@@ -93,4 +95,5 @@
     uHostCaps = 0;
 #endif
+    uDevCaps = 0;
 
     /* Confirm a successful initialization */
@@ -120,7 +123,31 @@
 }
 
+
 // IMouse properties
 /////////////////////////////////////////////////////////////////////////////
 
+int Mouse::getVMMDevMouseCaps(uint32_t *pfCaps)
+{
+    AssertPtrReturn(pfCaps, E_POINTER);
+    VMMDev *pVMMDev = mParent->getVMMDev();
+    ComAssertRet(pVMMDev, E_FAIL);
+    PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
+    ComAssertRet(pVMMDevPort, E_FAIL);
+
+    int rc = pVMMDevPort->pfnQueryMouseCapabilities(pVMMDevPort, pfCaps);
+    return RT_SUCCESS(rc) ? S_OK : E_FAIL;
+}
+
+int Mouse::setVMMDevMouseCaps(uint32_t fCaps)
+{
+    VMMDev *pVMMDev = mParent->getVMMDev();
+    ComAssertRet(pVMMDev, E_FAIL);
+    PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
+    ComAssertRet(pVMMDevPort, E_FAIL);
+
+    int rc = pVMMDevPort->pfnSetMouseCapabilities(pVMMDevPort, fCaps);
+    return RT_SUCCESS(rc) ? S_OK : E_FAIL;
+}
+
 /**
  * Returns whether the current setup can accept absolute mouse
@@ -140,27 +167,28 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    CHECK_CONSOLE_DRV (mpDrv);
-
-    ComAssertRet(mParent->getVMMDev(), E_FAIL);
-    ComAssertRet(mParent->getVMMDev()->getVMMDevPort(), E_FAIL);
-
-    *absoluteSupported = FALSE;
-    uint32_t mouseCaps;
-    mParent->getVMMDev()->getVMMDevPort()->pfnQueryMouseCapabilities(mParent->getVMMDev()->getVMMDevPort(), &mouseCaps);
-    *absoluteSupported = mouseCaps & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
-
-    return S_OK;
-}
-
-/**
- * Returns whether the current setup can accept relative mouse
- * events.
+    if (uDevCaps & MOUSE_DEVCAP_ABSOLUTE)
+        *absoluteSupported = TRUE;
+    else
+    {
+        CHECK_CONSOLE_DRV (mpDrv);
+
+        uint32_t mouseCaps;
+        int rc = getVMMDevMouseCaps(&mouseCaps);
+        AssertComRCReturn(rc, rc);
+        *absoluteSupported = mouseCaps & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE;
+    }
+
+    return S_OK;
+}
+
+/**
+ * Returns whether the guest can currently draw the mouse cursor itself.
  *
  * @returns COM status code
  * @param absoluteSupported address of result variable
  */
-STDMETHODIMP Mouse::COMGETTER(NeedsHostCursor) (BOOL *needsHostCursor)
-{
-    if (!needsHostCursor)
+STDMETHODIMP Mouse::COMGETTER(NeedsHostCursor) (BOOL *pfNeedsHostCursor)
+{
+    if (!pfNeedsHostCursor)
         return E_POINTER;
 
@@ -172,12 +200,9 @@
     CHECK_CONSOLE_DRV (mpDrv);
 
-    ComAssertRet(mParent->getVMMDev(), E_FAIL);
-    ComAssertRet(mParent->getVMMDev()->getVMMDevPort(), E_FAIL);
-
-    *needsHostCursor = FALSE;
-    uint32_t mouseCaps;
-    mParent->getVMMDev()->getVMMDevPort()->pfnQueryMouseCapabilities(mParent->getVMMDev()->getVMMDevPort(), &mouseCaps);
-    *needsHostCursor = mouseCaps & VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR;
-
+    uint32_t fMouseCaps;
+    int rc = getVMMDevMouseCaps(&fMouseCaps);
+    AssertComRCReturn(rc, rc);
+    *pfNeedsHostCursor = !!(  fMouseCaps
+                            & VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
     return S_OK;
 }
@@ -186,44 +211,6 @@
 /////////////////////////////////////////////////////////////////////////////
 
-/**
- * Send a mouse event.
- *
- * @returns COM status code
- * @param dx          X movement
- * @param dy          Y movement
- * @param dz          Z movement
- * @param buttonState The mouse button state
- */
-STDMETHODIMP Mouse::PutMouseEvent(LONG dx, LONG dy, LONG dz, LONG dw, LONG buttonState)
-{
-    HRESULT rc = S_OK;
-
-    AutoCaller autoCaller(this);
-    if (FAILED(autoCaller.rc())) return autoCaller.rc();
-
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    CHECK_CONSOLE_DRV (mpDrv);
-
-    ComAssertRet(mParent->getVMMDev(), E_FAIL);
-    ComAssertRet(mParent->getVMMDev()->getVMMDevPort(), E_FAIL);
-
-    uint32_t mouseCaps;
-    LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d\n", __PRETTY_FUNCTION__,
-             dx, dy, dz, dw));
-    mParent->getVMMDev()->getVMMDevPort()
-        ->pfnQueryMouseCapabilities(mParent->getVMMDev()->getVMMDevPort(),
-                                    &mouseCaps);
-    /*
-     * This method being called implies that the host no
-     * longer wants to use absolute coordinates. If the VMM
-     * device isn't aware of that yet, tell it.
-     */
-    if (mouseCaps & VMMDEV_MOUSE_HOST_CAN_ABSOLUTE)
-    {
-        mParent->getVMMDev()->getVMMDevPort()->pfnSetMouseCapabilities(
-            mParent->getVMMDev()->getVMMDevPort(), uHostCaps);
-    }
-
+static uint32_t mouseButtonsToPDM(LONG buttonState)
+{
     uint32_t fButtons = 0;
     if (buttonState & MouseButtonState_LeftButton)
@@ -237,13 +224,164 @@
     if (buttonState & MouseButtonState_XButton2)
         fButtons |= PDMIMOUSEPORT_BUTTON_X2;
-
-    int vrc = mpDrv->pUpPort->pfnPutEvent(mpDrv->pUpPort, dx, dy, dz, dw, fButtons);
-    if (RT_FAILURE(vrc))
-        rc = setError(VBOX_E_IPRT_ERROR,
-                      tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
-                      vrc);
+    return fButtons;
+}
+
+
+/**
+ * Send a relative event to the mouse device.
+ *
+ * @returns   COM status code
+ */
+int Mouse::reportRelEventToMouseDev(int32_t dx, int32_t dy, int32_t dz,
+                                    int32_t dw, uint32_t fButtons)
+{
+    CHECK_CONSOLE_DRV (mpDrv);
+
+    if (dx || dy || dz || dw || fButtons != mLastButtons)
+    {
+        PPDMIMOUSEPORT pUpPort = mpDrv->pUpPort;
+        int vrc = pUpPort->pfnPutEvent(pUpPort, dx, dy, dz, dw, fButtons);
+
+        if (RT_FAILURE(vrc))
+            setError(VBOX_E_IPRT_ERROR,
+                     tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
+                     vrc);
+        AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
+    }
+    return S_OK;
+}
+
+
+/**
+ * Send an absolute position event to the mouse device.
+ *
+ * @returns   COM status code
+ */
+int Mouse::reportAbsEventToMouseDev(uint32_t mouseXAbs, uint32_t mouseYAbs)
+{
+    CHECK_CONSOLE_DRV (mpDrv);
+
+    if (mouseXAbs != mLastAbsX || mouseYAbs != mLastAbsY)
+    {
+        int vrc = mpDrv->pUpPort->pfnPutEventAbs(mpDrv->pUpPort, mouseXAbs,
+                                                 mouseYAbs);
+        if (RT_FAILURE(vrc))
+            setError(VBOX_E_IPRT_ERROR,
+                     tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
+                     vrc);
+        AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
+    }
+    return S_OK;
+}
+
+
+/**
+ * Send an absolute position event to the VMM device.
+ *
+ * @returns   COM status code
+ */
+int Mouse::reportAbsEventToVMMDev(uint32_t mouseXAbs, uint32_t mouseYAbs)
+{
+    VMMDev *pVMMDev = mParent->getVMMDev();
+    ComAssertRet(pVMMDev, E_FAIL);
+    PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
+    ComAssertRet(pVMMDevPort, E_FAIL);
+
+    if (mouseXAbs != mLastAbsX || mouseYAbs != mLastAbsY)
+    {
+        int vrc = pVMMDevPort->pfnSetAbsoluteMouse(pVMMDevPort,
+                                                   mouseXAbs, mouseYAbs);
+        if (RT_FAILURE(vrc))
+            setError(VBOX_E_IPRT_ERROR,
+                     tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
+                     vrc);
+        AssertRCReturn(vrc, VBOX_E_IPRT_ERROR);
+    }
+    return S_OK;
+}
+
+/**
+ * Send a mouse event.
+ *
+ * @returns COM status code
+ * @param dx          X movement
+ * @param dy          Y movement
+ * @param dz          Z movement
+ * @param buttonState The mouse button state
+ */
+STDMETHODIMP Mouse::PutMouseEvent(LONG dx, LONG dy, LONG dz, LONG dw, LONG buttonState)
+{
+    HRESULT rc = S_OK;
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    CHECK_CONSOLE_DRV (mpDrv);
+
+    LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d\n", __PRETTY_FUNCTION__,
+             dx, dy, dz, dw));
+    if (!(uDevCaps & MOUSE_DEVCAP_ABSOLUTE))
+    {
+        /*
+         * This method being called implies that the host no
+         * longer wants to use absolute coordinates. If the VMM
+         * device isn't aware of that yet, tell it.
+         */
+        uint32_t mouseCaps;
+        rc = getVMMDevMouseCaps(&mouseCaps);
+        ComAssertComRCRet(rc, rc);
+
+        if (mouseCaps & VMMDEV_MOUSE_HOST_CAN_ABSOLUTE)
+            setVMMDevMouseCaps(uHostCaps);
+    }
+
+    uint32_t fButtons = mouseButtonsToPDM(buttonState);
+    rc = reportRelEventToMouseDev(dx, dy, dz, dw, fButtons);
+    if (SUCCEEDED(rc))
+        mLastButtons = fButtons;
 
     return rc;
 }
+
+/**
+ * Convert an X value in screen co-ordinates to a value from 0 to 0xffff
+ *
+ * @returns   COM status value
+ */
+int Mouse::convertDisplayWidth(LONG x, uint32_t *pcX)
+{
+    AssertPtrReturn(pcX, E_POINTER);
+    Display *pDisplay = mParent->getDisplay();
+    ComAssertRet(pDisplay, E_FAIL);
+
+    ULONG displayWidth;
+    int rc = pDisplay->COMGETTER(Width)(&displayWidth);
+    ComAssertComRCRet(rc, rc);
+
+    *pcX = displayWidth ? (x * 0xFFFF) / displayWidth: 0;
+    return S_OK;
+}
+
+/**
+ * Convert a Y value in screen co-ordinates to a value from 0 to 0xffff
+ *
+ * @returns   COM status value
+ */
+int Mouse::convertDisplayHeight(LONG y, uint32_t *pcY)
+{
+    AssertPtrReturn(pcY, E_POINTER);
+    Display *pDisplay = mParent->getDisplay();
+    ComAssertRet(pDisplay, E_FAIL);
+
+    ULONG displayHeight;
+    int rc = pDisplay->COMGETTER(Height)(&displayHeight);
+    ComAssertComRCRet(rc, rc);
+
+    *pcY = displayHeight ? (y * 0xFFFF) / displayHeight: 0;
+    return S_OK;
+}
+
 
 /**
@@ -267,84 +405,52 @@
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    CHECK_CONSOLE_DRV (mpDrv);
-
-    ComAssertRet(mParent->getVMMDev(), E_FAIL);
-    ComAssertRet(mParent->getVMMDev()->getVMMDevPort(), E_FAIL);
-
-    uint32_t mouseCaps;
     LogRel3(("%s: x=%d, y=%d, dz=%d, dw=%d\n", __PRETTY_FUNCTION__,
              x, y, dz, dw));
-    mParent->getVMMDev()->getVMMDevPort()
-        ->pfnQueryMouseCapabilities(mParent->getVMMDev()->getVMMDevPort(),
-                                    &mouseCaps);
-    /*
-     * This method being called implies that the host wants
-     * to use absolute coordinates. If the VMM device isn't
-     * aware of that yet, tell it.
-     */
-    if (!(mouseCaps & VMMDEV_MOUSE_HOST_CAN_ABSOLUTE))
-    {
-        mParent->getVMMDev()->getVMMDevPort()->pfnSetMouseCapabilities(
-            mParent->getVMMDev()->getVMMDevPort(),
-            uHostCaps | VMMDEV_MOUSE_HOST_CAN_ABSOLUTE);
-    }
-
-    Display *pDisplay = mParent->getDisplay();
-    ComAssertRet(pDisplay, E_FAIL);
-
-    ULONG displayWidth;
-    ULONG displayHeight;
-    rc = pDisplay->COMGETTER(Width)(&displayWidth);
+
+    uint32_t mouseXAbs;
+    rc = convertDisplayWidth(x, &mouseXAbs);
     ComAssertComRCRet(rc, rc);
-    rc = pDisplay->COMGETTER(Height)(&displayHeight);
+    uint32_t mouseYAbs;
+    rc = convertDisplayHeight(y, &mouseYAbs);
     ComAssertComRCRet(rc, rc);
-
-    uint32_t mouseXAbs = displayWidth? (x * 0xFFFF) / displayWidth: 0;
-    uint32_t mouseYAbs = displayHeight? (y * 0xFFFF) / displayHeight: 0;
-
-    /*
-     * Send the absolute mouse position to the VMM device.
-     */
-    int vrc = mParent->getVMMDev()->getVMMDevPort()
-        ->pfnSetAbsoluteMouse(mParent->getVMMDev()->getVMMDevPort(),
-                              mouseXAbs, mouseYAbs);
-    ComAssertRCRet (vrc, E_FAIL);
-
-    // Check if the guest actually wants absolute mouse positions.
-    if (mouseCaps & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
-    {
-        uint32_t fButtons = 0;
-        if (buttonState & MouseButtonState_LeftButton)
-            fButtons |= PDMIMOUSEPORT_BUTTON_LEFT;
-        if (buttonState & MouseButtonState_RightButton)
-            fButtons |= PDMIMOUSEPORT_BUTTON_RIGHT;
-        if (buttonState & MouseButtonState_MiddleButton)
-            fButtons |= PDMIMOUSEPORT_BUTTON_MIDDLE;
-        if (buttonState & MouseButtonState_XButton1)
-            fButtons |= PDMIMOUSEPORT_BUTTON_X1;
-        if (buttonState & MouseButtonState_XButton2)
-            fButtons |= PDMIMOUSEPORT_BUTTON_X2;
-
-        /* This is a workaround.  In order to alert the Guest Additions to the
-         * fact that the absolute pointer position has changed, we send a
-         * a minute movement event to the PS/2 mouse device.  But in order
-         * to avoid the mouse jiggling every time the use clicks, we check to
-         * see if the position has really changed since the last mouse event.
+    uint32_t fButtons = mouseButtonsToPDM(buttonState);
+    /* Older guest additions rely on a small phony movement event on the
+     * PS/2 device to notice absolute events. */
+    bool fNeedsJiggle = false;
+
+    if (uDevCaps & MOUSE_DEVCAP_ABSOLUTE)
+        rc = reportAbsEventToMouseDev(mouseXAbs, mouseYAbs);
+    else
+    {
+        uint32_t mouseCaps;
+        rc = getVMMDevMouseCaps(&mouseCaps);
+        ComAssertComRCRet(rc, rc);
+        /*
+         * This method being called implies that the host wants
+         * to use absolute coordinates. If the VMM device isn't
+         * aware of that yet, tell it.
          */
-        if (   ((mLastAbsX == mouseXAbs) && (mLastAbsY == mouseYAbs))
-            || (mouseCaps & VMMDEV_MOUSE_GUEST_USES_VMMDEV))
-            vrc = mpDrv->pUpPort->pfnPutEvent(mpDrv->pUpPort, 0, 0, dz, dw,
-                                              fButtons);
-        else
-            vrc = mpDrv->pUpPort->pfnPutEvent(mpDrv->pUpPort, 1, 1, dz, dw,
-                                              fButtons);
-        mLastAbsX = mouseXAbs;
-        mLastAbsY = mouseYAbs;
-        if (RT_FAILURE(vrc))
-            rc = setError(VBOX_E_IPRT_ERROR,
-                          tr("Could not send the mouse event to the virtual mouse (%Rrc)"),
-                          vrc);
-    }
-
+        if (!(mouseCaps & VMMDEV_MOUSE_HOST_CAN_ABSOLUTE))
+            setVMMDevMouseCaps(uHostCaps | VMMDEV_MOUSE_HOST_CAN_ABSOLUTE);
+
+        /*
+         * Send the absolute mouse position to the VMM device.
+         */
+        rc = reportAbsEventToVMMDev(mouseXAbs, mouseYAbs);
+        fNeedsJiggle = !(mouseCaps & VMMDEV_MOUSE_GUEST_USES_VMMDEV);
+    }
+    ComAssertComRCRet (rc, rc);
+    mLastAbsX = mouseXAbs;
+    mLastAbsY = mouseYAbs;
+    /* We may need to send a relative event for button information or to
+     * wake the guest up to the changed absolute co-ordinates. */
+    /* If the event is a pure wake up one, we make sure it contains some
+     * (possibly phony) event data to make sure it isn't just discarded on
+     * the way.  Note: we ignore dw as it is optional. */
+    if (fNeedsJiggle || fButtons != mLastButtons || dz || dw)
+        rc = reportRelEventToMouseDev(fNeedsJiggle ? 1 : 0, 0, dz, dw,
+                                      fButtons);
+    if (SUCCEEDED(rc))
+        mLastButtons = fButtons;
     return rc;
 }
@@ -384,4 +490,26 @@
         pData->pMouse->mpDrv = NULL;
     }
+}
+
+
+DECLCALLBACK(void) Mouse::mouseAbsModeChange (PPDMIMOUSECONNECTOR pInterface, bool fAbs)
+{
+    PDRVMAINMOUSE pDrv = PPDMIMOUSECONNECTOR_2_MAINMOUSE (pInterface);
+    if (fAbs)
+        pDrv->pMouse->uDevCaps |= MOUSE_DEVCAP_ABSOLUTE;
+    else
+        pDrv->pMouse->uDevCaps &= ~MOUSE_DEVCAP_ABSOLUTE;
+    /** @todo we have to hack around the fact that VMMDev may not be
+     * initialised too close to startup.  The real fix is to change the
+     * protocol for onMouseCapabilityChange so that we no longer need to
+     * query VMMDev, but that requires more changes that I want to do in
+     * the next commit, so it must be put off until the followup one. */
+    uint32_t fMouseCaps = 0;
+    int rc = S_OK;
+    if (   pDrv->pMouse->mParent->getVMMDev()
+        && pDrv->pMouse->mParent->getVMMDev()->mpDrv)
+        rc = pDrv->pMouse->getVMMDevMouseCaps(&fMouseCaps);
+    AssertComRCReturnVoid(rc);
+    pDrv->pMouse->getParent()->onMouseCapabilityChange (fAbs, fMouseCaps & VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR);
 }
 
@@ -411,4 +539,6 @@
      */
     pDrvIns->IBase.pfnQueryInterface        = Mouse::drvQueryInterface;
+
+    pData->IConnector.pfnAbsModeChange      = Mouse::mouseAbsModeChange;
 
     /*
Index: /trunk/src/VBox/Main/include/MouseImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/MouseImpl.h	(revision 26623)
+++ /trunk/src/VBox/Main/include/MouseImpl.h	(revision 26624)
@@ -46,4 +46,9 @@
 typedef ConsoleEventBuffer<MouseEvent> MouseEventBuffer;
 
+enum
+{
+    MOUSE_DEVCAP_ABSOLUTE = 1
+};
+
 class ATL_NO_VTABLE Mouse :
     public VirtualBoxBase,
@@ -90,9 +95,25 @@
     static const PDMDRVREG  DrvReg;
 
+    Console *getParent() const
+    {
+        return mParent;
+    }
+
 private:
 
     static DECLCALLBACK(void *) drvQueryInterface(PPDMIBASE pInterface, const char *pszIID);
+    static DECLCALLBACK(void)   mouseAbsModeChange (PPDMIMOUSECONNECTOR pInterface, bool fAbs);
     static DECLCALLBACK(int)    drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags);
     static DECLCALLBACK(void)   drvDestruct(PPDMDRVINS pDrvIns);
+    
+    int getVMMDevMouseCaps(uint32_t *pfCaps);
+    int setVMMDevMouseCaps(uint32_t fCaps);
+    int reportRelEventToMouseDev(int32_t dx, int32_t dy, int32_t dz,
+                                 int32_t dw, uint32_t fButtons);
+    int reportAbsEventToMouseDev(uint32_t mouseXAbs, uint32_t mouseYAbs);
+    int reportAbsEventToVMMDev(uint32_t mouseXAbs, uint32_t mouseYAbs);
+    int convertDisplayWidth(LONG x, uint32_t *pcX);
+    int convertDisplayHeight(LONG y, uint32_t *pcY);
+    bool needsRelativeEvent(uint32_t cXAbs, uint32_t cYAbs, int32_t dz, int32_t dw, uint32_t fButtons, uint32_t fCaps);
 
     const ComObjPtr<Console, ComWeakRef> mParent;
@@ -101,6 +122,8 @@
 
     LONG uHostCaps;
+    LONG uDevCaps;
     uint32_t mLastAbsX;
     uint32_t mLastAbsY;
+    uint32_t mLastButtons;
 };
 
