Index: trunk/src/VBox/Additions/linux/drm/README.testing
===================================================================
--- trunk/src/VBox/Additions/linux/drm/README.testing	(revision 64830)
+++ trunk/src/VBox/Additions/linux/drm/README.testing	(revision 64919)
@@ -1,4 +1,13 @@
-This document lists things which have been known to fail in the past with the drm video driver and which should be tested regularly, e.g. when making code changes or before releases.
+This document lists things which have been known to fail in the past with the
+drm video driver and which should be tested regularly, e.g. when making code
+changes or before releases.
 
  * Test that "auto-resize" is enabled in the GUI if user space supports it.
- * Test that old versions of Plymouth which do not report rectangles (pre-0.9.0/2014-05-20) update the screen correctly.
+ * Test that old versions of Plymouth which do not report rectangles
+   (pre-0.9.0/2014-05-20) update the screen correctly.
+ * Having valid, non-overlapping offset hints on all screens has caused
+   mouse integration and/or screen updates to break for fbdev, X.Org or
+   GNOME Shell/Wayland.
+ * Note that if a multi-screen VM is booted with only one screen enabled
+   the fbdev console will be disabled on the others until reboot.  Test this
+   configuration.
Index: trunk/src/VBox/Additions/linux/drm/vbox_drv.h
===================================================================
--- trunk/src/VBox/Additions/linux/drm/vbox_drv.h	(revision 64830)
+++ trunk/src/VBox/Additions/linux/drm/vbox_drv.h	(revision 64919)
@@ -97,5 +97,4 @@
     bool any_pitch;
     unsigned num_crtcs;
-    bool vga2_clone;
     /** Amount of available VRAM, including space used for buffers. */
     uint32_t full_vram_size;
@@ -129,4 +128,7 @@
     uint32_t input_mapping_width;
     uint32_t input_mapping_height;
+    /** Is user-space using an X.Org-style layout of one large frame-buffer
+     * encompassing all screen ones or is the fbdev console active? */
+    bool single_framebuffer;
     uint32_t cursor_width;
     uint32_t cursor_height;
@@ -169,4 +171,6 @@
     uint32_t fb_offset;
     bool cursor_enabled;
+    uint16_t x_hint;
+    uint16_t y_hint;
 };
 
Index: trunk/src/VBox/Additions/linux/drm/vbox_irq.c
===================================================================
--- trunk/src/VBox/Additions/linux/drm/vbox_irq.c	(revision 64830)
+++ trunk/src/VBox/Additions/linux/drm/vbox_irq.c	(revision 64919)
@@ -85,6 +85,42 @@
 }
 
+/** Check that the position hints provided by the host are suitable for GNOME
+ * shell (i.e. all screens disjoint and hints for all enabled screens) and if
+ * not replace them with default ones.  Providing valid hints improves the
+ * chances that we will get a known screen layout for pointer mapping. */
+static void validate_or_set_position_hints(struct vbox_private *vbox)
+{
+    int i, j;
+    uint16_t currentx = 0;
+    bool valid = true;
+
+    for (i = 0; i < vbox->num_crtcs; ++i) {
+        for (j = 0; j < i; ++j) {
+            struct VBVAMODEHINT *hintsi = &vbox->last_mode_hints[i];
+            struct VBVAMODEHINT *hintsj = &vbox->last_mode_hints[j];
+
+            if (hintsi->fEnabled && hintsj->fEnabled) {
+                if ((hintsi->dx >= 0xffff || hintsi->dy >= 0xffff ||
+                     hintsj->dx >= 0xffff || hintsj->dy >= 0xffff) ||
+                    (hintsi->dx < hintsj->dx + (hintsj->cx & 0x8fff) &&
+                     hintsi->dx + (hintsi->cx & 0x8fff) > hintsj->dx) ||
+                    (hintsi->dy < hintsj->dy + (hintsj->cy & 0x8fff) &&
+                     hintsi->dy + (hintsi->cy & 0x8fff) > hintsj->dy))
+                    valid = false;
+            }
+        }
+    }
+    if (!valid)
+        for (i = 0; i < vbox->num_crtcs; ++i) {
+            if (vbox->last_mode_hints[i].fEnabled) {
+                vbox->last_mode_hints[i].dx = currentx;
+                vbox->last_mode_hints[i].dy = 0;
+                currentx += vbox->last_mode_hints[i].cx & 0x8fff;
+            }
+        }
+}
+
 /**
- * Query the host for
+ * Query the host for the most recent video mode hints.
  */
 static void vbox_update_mode_hints(struct vbox_private *vbox)
@@ -105,4 +141,5 @@
         return;
     }
+    validate_or_set_position_hints(vbox);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
     drm_modeset_lock_all(dev);
@@ -120,4 +157,6 @@
             vbox_connector->mode_hint.width = hints->cx & 0x8fff;
             vbox_connector->mode_hint.height = hints->cy & 0x8fff;
+            vbox_connector->vbox_crtc->x_hint = hints->dx;
+            vbox_connector->vbox_crtc->y_hint = hints->dy;
             vbox_connector->mode_hint.disconnected = disconnected;
             if (vbox_connector->vbox_crtc->disconnected != disconnected) {
@@ -127,12 +166,4 @@
                 vbox_connector->vbox_crtc->disconnected = disconnected;
             }
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
-            if ((hints->dx < 0xffff) && (hints->dy < 0xffff)) {
-                drm_object_property_set_value(&connector->base,
-                    dev->mode_config.suggested_x_property, hints->dx & 0x8fff);
-                drm_object_property_set_value(&connector->base,
-                    dev->mode_config.suggested_y_property, hints->dy & 0x8fff);
-            }
-#endif
         }
     }
Index: trunk/src/VBox/Additions/linux/drm/vbox_mode.c
===================================================================
--- trunk/src/VBox/Additions/linux/drm/vbox_mode.c	(revision 64830)
+++ trunk/src/VBox/Additions/linux/drm/vbox_mode.c	(revision 64919)
@@ -74,4 +74,5 @@
     unsigned crtc_id;
     uint16_t flags;
+    int32_t x_offset, y_offset;
 
     vbox = crtc->dev->dev_private;
@@ -85,4 +86,6 @@
     pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8;
 #endif
+    x_offset = vbox->single_framebuffer ? crtc->x : vbox_crtc->x_hint;
+    y_offset = vbox->single_framebuffer ? crtc->y : vbox_crtc->y_hint;
     /* This is the old way of setting graphics modes.  It assumed one screen
      * and a frame-buffer at the start of video RAM.  On older versions of
@@ -101,5 +104,5 @@
     flags |= (vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0);
     VBoxHGSMIProcessDisplayInfo(&vbox->submit_info, vbox_crtc->crtc_id,
-                                crtc->x, crtc->y,
+                                x_offset, y_offset,
                                 crtc->x * bpp / 8 + crtc->y * pitch,
                                 pitch, width, height,
@@ -171,4 +174,54 @@
 }
 
+/* Try to map the layout of virtual screens to the range of the input device.
+ * Return true if we need to re-set the crtc modes due to screen offset
+ * changes. */
+static bool vbox_set_up_input_mapping(struct vbox_private *vbox)
+{
+    struct drm_crtc *crtci;
+    struct drm_connector *connectori;
+    struct drm_framebuffer *fb1 = NULL;
+    bool single_framebuffer = true;
+    bool old_single_framebuffer = vbox->single_framebuffer;
+    uint16_t width = 0, height = 0;
+
+    /* Are we using an X.Org-style single large frame-buffer for all crtcs?
+     * If so then screen layout can be deduced from the crtc offsets.
+     * Same fall-back if this is the fbdev frame-buffer. */
+    list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
+        if (fb1 == NULL) {
+            fb1 = CRTC_FB(crtci);
+            if (to_vbox_framebuffer(fb1) == &vbox->fbdev->afb)
+                break;
+        } else if (CRTC_FB(crtci) != NULL && fb1 != CRTC_FB(crtci))
+            single_framebuffer = false;
+    }
+    if (single_framebuffer) {
+        list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
+            if (to_vbox_crtc(crtci)->crtc_id == 0) {
+                vbox->single_framebuffer = true;
+                vbox->input_mapping_width = CRTC_FB(crtci)->width;
+                vbox->input_mapping_height = CRTC_FB(crtci)->height;
+                return old_single_framebuffer != vbox->single_framebuffer;
+            }
+        }
+    }
+    /* Otherwise calculate the total span of all screens. */
+    list_for_each_entry(connectori, &vbox->dev->mode_config.connector_list,
+                        head) {
+        struct vbox_connector *vbox_connector = to_vbox_connector(connectori);
+        struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc;
+
+        width = max(width, (uint16_t) (vbox_crtc->x_hint +
+                    vbox_connector->mode_hint.width));
+        height = max(height, (uint16_t) (vbox_crtc->y_hint +
+                    vbox_connector->mode_hint.height));
+    }
+    vbox->single_framebuffer = false;
+    vbox->input_mapping_width = width;
+    vbox->input_mapping_height = height;
+    return old_single_framebuffer != vbox->single_framebuffer;
+}
+
 static int vbox_crtc_do_set_base(struct drm_crtc *crtc,
                 struct drm_framebuffer *old_fb,
@@ -215,7 +268,11 @@
     /* vbox_set_start_address_crt1(crtc, (u32)gpu_addr); */
     vbox_crtc->fb_offset = gpu_addr;
-    if (vbox_crtc->crtc_id == 0) {
-        vbox->input_mapping_width = CRTC_FB(crtc)->width;
-        vbox->input_mapping_height = CRTC_FB(crtc)->height;
+    if (vbox_set_up_input_mapping(vbox)) {
+        struct drm_crtc *crtci;
+
+        list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) {
+            vbox_set_view(crtc);
+            vbox_do_modeset(crtci, &crtci->mode);
+        }
     }
     return 0;
@@ -242,5 +299,4 @@
     if (!rc)
         vbox_do_modeset(crtc, mode);
-    /* Note that the input mapping is always relative to the first screen. */
     VBoxHGSMIUpdateInputMapping(&vbox->submit_info, 0, 0,
                                 vbox->input_mapping_width,
@@ -505,4 +561,12 @@
     }
     vbox_set_edid(connector, preferred_width, preferred_height);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    drm_object_property_set_value(&connector->base,
+        vbox->dev->mode_config.suggested_x_property,
+        vbox_connector->vbox_crtc->x_hint);
+    drm_object_property_set_value(&connector->base,
+        vbox->dev->mode_config.suggested_y_property,
+        vbox_connector->vbox_crtc->y_hint);
+#endif
     return num_modes;
 }
@@ -592,7 +656,7 @@
     drm_mode_create_suggested_offset_properties(dev);
     drm_object_attach_property(&connector->base,
-                               dev->mode_config.suggested_x_property, 0);
+                               dev->mode_config.suggested_x_property, -1);
     drm_object_attach_property(&connector->base,
-                               dev->mode_config.suggested_y_property, 0);
+                               dev->mode_config.suggested_y_property, -1);
 #endif
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
@@ -763,4 +827,6 @@
                       | VBOX_MOUSE_POINTER_SHAPE
                       | VBOX_MOUSE_POINTER_ALPHA;
+    int32_t crtc_x = vbox->single_framebuffer ? crtc->x : to_vbox_crtc(crtc)->x_hint;
+    int32_t crtc_y = vbox->single_framebuffer ? crtc->y : to_vbox_crtc(crtc)->y_hint;
     uint32_t host_x, host_y;
     uint32_t hot_x = 0;
@@ -769,8 +835,8 @@
 
     /* We compare these to unsigned later and don't need to handle negative. */
-    if (x + crtc->x < 0 || y + crtc->y < 0 || vbox->cursor_data_size == 0)
+    if (x + crtc_x < 0 || y + crtc_y < 0 || vbox->cursor_data_size == 0)
         return 0;
-    rc = VBoxHGSMICursorPosition(&vbox->submit_info, true, x + crtc->x,
-                                 y + crtc->y, &host_x, &host_y);
+    rc = VBoxHGSMICursorPosition(&vbox->submit_info, true, x + crtc_x,
+                                 y + crtc_y, &host_x, &host_y);
     /* Work around a bug after save and restore in 5.0.20 and earlier. */
     if (RT_FAILURE(rc) || (host_x == 0 && host_y == 0))
@@ -778,8 +844,8 @@
                : rc == VERR_NO_MEMORY ? -ENOMEM
                : -EINVAL;
-    if (x + crtc->x < host_x)
-        hot_x = min(host_x - x - crtc->x, vbox->cursor_width);
-    if (y + crtc->y < host_y)
-        hot_y = min(host_y - y - crtc->y, vbox->cursor_height);
+    if (x + crtc_x < host_x)
+        hot_x = min(host_x - x - crtc_x, vbox->cursor_width);
+    if (y + crtc_y < host_y)
+        hot_y = min(host_y - y - crtc_y, vbox->cursor_height);
     if (hot_x == vbox->cursor_hot_x && hot_y == vbox->cursor_hot_y)
         return 0;
