From 11512d6a2cd6d816a13db7ee0c48627d6534b377 Mon Sep 17 00:00:00 2001
From: Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
Date: Fri, 22 May 2015 20:30:59 +0200
Subject: [PATCH] eglfs_kms: Virtual desktop

Now we can choose to use separate screens or virtual desktop.

With virtual desktop the geometry of all screens is taken
into account so that the input plugin do not clamp global
pointer coordinates to the first screen anymore, we also
create only one hardware cursor that can now freely move
on all the screens.

Virtual desktop is enabled by default, but the old default
behavior can be restored by setting separateScreens to true.

Change-Id: I78dbf9e8d3dd44f68d33350dc8fc3727bf8a26fe
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
---
 .../eglfs_kms/qeglfskmscursor.cpp             | 45 +++++++++++++------
 .../eglfs_kms/qeglfskmsdevice.cpp             | 25 +++++++++++
 .../eglfs_kms/qeglfskmsdevice.h               |  6 +++
 .../eglfs_kms/qeglfskmsintegration.cpp        |  9 ++++
 .../eglfs_kms/qeglfskmsintegration.h          |  3 ++
 .../eglfs_kms/qeglfskmsscreen.cpp             |  5 +++
 .../eglfs_kms/qeglfskmsscreen.h               |  7 +++
 7 files changed, 86 insertions(+), 14 deletions(-)

diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmscursor.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmscursor.cpp
index cd92c49ff1..fe47c947b4 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmscursor.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmscursor.cpp
@@ -1,5 +1,6 @@
 /****************************************************************************
 **
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
@@ -87,8 +88,11 @@ QEglFSKmsCursor::QEglFSKmsCursor(QEglFSKmsScreen *screen)
 
 QEglFSKmsCursor::~QEglFSKmsCursor()
 {
-    drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0);
-    drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0);
+    Q_FOREACH (QPlatformScreen *screen, m_screen->virtualSiblings()) {
+        QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
+        drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0, 0);
+        drmModeMoveCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0);
+    }
 
     gbm_bo_destroy(m_bo);
     m_bo = Q_NULLPTR;
@@ -143,10 +147,15 @@ void QEglFSKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window)
     gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.byteCount());
 
     uint32_t handle = gbm_bo_get_handle(m_bo).u32;
-    int status = drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, handle,
-                                  m_cursorSize.width(), m_cursorSize.height());
-    if (status != 0)
-        qWarning("Could not set cursor: %d", status);
+
+    Q_FOREACH (QPlatformScreen *screen, m_screen->virtualSiblings()) {
+        QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
+
+        int status = drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, handle,
+                                      m_cursorSize.width(), m_cursorSize.height());
+        if (status != 0)
+            qWarning("Could not set cursor on screen %s: %d", kmsScreen->name().toLatin1().constData(), status);
+    }
 }
 #endif // QT_NO_CURSOR
 
@@ -157,12 +166,17 @@ QPoint QEglFSKmsCursor::pos() const
 
 void QEglFSKmsCursor::setPos(const QPoint &pos)
 {
-    QPoint adjustedPos = pos - m_cursorImage.hotspot();
-    int ret = drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, adjustedPos.x(), adjustedPos.y());
-    if (ret == 0) {
-        m_pos = pos;
-    } else {
-        qWarning("Failed to move cursor: %d", ret);
+    Q_FOREACH (QPlatformScreen *screen, m_screen->virtualSiblings()) {
+        QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
+        QPoint origin = kmsScreen->geometry().topLeft();
+        QPoint localPos = pos - origin;
+        QPoint adjustedPos = localPos - m_cursorImage.hotspot();
+
+        int ret = drmModeMoveCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, adjustedPos.x(), adjustedPos.y());
+        if (ret == 0)
+            m_pos = pos;
+        else
+            qWarning("Failed to move cursor on screen %s: %d", kmsScreen->name().toLatin1().constData(), ret);
     }
 }
 
@@ -176,8 +190,11 @@ void QEglFSKmsCursor::initCursorAtlas()
 
     QFile file(QString::fromUtf8(json));
     if (!file.open(QFile::ReadOnly)) {
-        drmModeSetCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0, 0);
-        drmModeMoveCursor(m_screen->device()->fd(), m_screen->output().crtc_id, 0, 0);
+        Q_FOREACH (QPlatformScreen *screen, m_screen->virtualSiblings()) {
+            QEglFSKmsScreen *kmsScreen = static_cast<QEglFSKmsScreen *>(screen);
+            drmModeSetCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0, 0);
+            drmModeMoveCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0);
+        }
         m_visible = false;
         return;
     }
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp
index bafe1e3324..18a66e34f5 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp
@@ -1,5 +1,6 @@
 /****************************************************************************
 **
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
@@ -308,6 +309,7 @@ QEglFSKmsDevice::QEglFSKmsDevice(QEglFSKmsIntegration *integration, const QStrin
     , m_gbm_device(Q_NULLPTR)
     , m_crtc_allocator(0)
     , m_connector_allocator(0)
+    , m_globalCursor(Q_NULLPTR)
 {
 }
 
@@ -347,6 +349,10 @@ void QEglFSKmsDevice::close()
         qt_safe_close(m_dri_fd);
         m_dri_fd = -1;
     }
+
+    if (m_globalCursor)
+        m_globalCursor->deleteLater();
+    m_globalCursor = Q_NULLPTR;
 }
 
 void QEglFSKmsDevice::createScreens()
@@ -357,6 +363,8 @@ void QEglFSKmsDevice::createScreens()
         return;
     }
 
+    QEglFSKmsScreen *primaryScreen = Q_NULLPTR;
+    QList<QPlatformScreen *> siblings;
     QPoint pos(0, 0);
     QEglFSIntegration *integration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration());
 
@@ -369,12 +377,24 @@ void QEglFSKmsDevice::createScreens()
         if (screen) {
             integration->addScreen(screen);
             pos.rx() += screen->geometry().width();
+            siblings << screen;
+
+            if (!primaryScreen)
+                primaryScreen = screen;
         }
 
         drmModeFreeConnector(connector);
     }
 
     drmModeFreeResources(resources);
+
+    if (!m_integration->separateScreens()) {
+        Q_FOREACH (QPlatformScreen *screen, siblings)
+            static_cast<QEglFSKmsScreen *>(screen)->setVirtualSiblings(siblings);
+
+        if (primaryScreen)
+            m_globalCursor = new QEglFSKmsCursor(primaryScreen);
+    }
 }
 
 gbm_device *QEglFSKmsDevice::device() const
@@ -387,6 +407,11 @@ int QEglFSKmsDevice::fd() const
     return m_dri_fd;
 }
 
+QPlatformCursor *QEglFSKmsDevice::globalCursor() const
+{
+    return m_globalCursor;
+}
+
 void QEglFSKmsDevice::handleDrmEvent()
 {
     drmEventContext drmEvent = {
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h
index 29a1332c9a..23fca934e5 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h
@@ -1,5 +1,6 @@
 /****************************************************************************
 **
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
@@ -34,6 +35,7 @@
 #ifndef QEGLFSKMSDEVICE_H
 #define QEGLFSKMSDEVICE_H
 
+#include "qeglfskmscursor.h"
 #include "qeglfskmsintegration.h"
 
 #include <xf86drm.h>
@@ -57,6 +59,8 @@ public:
     gbm_device *device() const;
     int fd() const;
 
+    QPlatformCursor *globalCursor() const;
+
     void handleDrmEvent();
 
 private:
@@ -70,6 +74,8 @@ private:
     quint32 m_crtc_allocator;
     quint32 m_connector_allocator;
 
+    QEglFSKmsCursor *m_globalCursor;
+
     int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector);
     QEglFSKmsScreen *screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos);
 
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp
index 7bb932cf00..45224ccb87 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp
@@ -1,5 +1,6 @@
 /****************************************************************************
 **
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
@@ -60,6 +61,7 @@ QEglFSKmsIntegration::QEglFSKmsIntegration()
     : m_device(Q_NULLPTR)
     , m_hwCursor(true)
     , m_pbuffers(false)
+    , m_separateScreens(false)
 {}
 
 void QEglFSKmsIntegration::platformInit()
@@ -203,6 +205,11 @@ bool QEglFSKmsIntegration::hwCursor() const
     return m_hwCursor;
 }
 
+bool QEglFSKmsIntegration::separateScreens() const
+{
+    return m_separateScreens;
+}
+
 QMap<QString, QVariantMap> QEglFSKmsIntegration::outputSettings() const
 {
     return m_outputSettings;
@@ -235,6 +242,7 @@ void QEglFSKmsIntegration::loadConfig()
     m_hwCursor = object.value(QStringLiteral("hwcursor")).toBool(m_hwCursor);
     m_pbuffers = object.value(QStringLiteral("pbuffers")).toBool(m_pbuffers);
     m_devicePath = object.value(QStringLiteral("device")).toString();
+    m_separateScreens = object.value(QStringLiteral("separateScreens")).toBool(m_separateScreens);
 
     const QJsonArray outputs = object.value(QStringLiteral("outputs")).toArray();
     for (int i = 0; i < outputs.size(); i++) {
@@ -254,6 +262,7 @@ void QEglFSKmsIntegration::loadConfig()
     qCDebug(qLcEglfsKmsDebug) << "Configuration:\n"
                               << "\thwcursor:" << m_hwCursor << "\n"
                               << "\tpbuffers:" << m_pbuffers << "\n"
+                              << "\tseparateScreens:" << m_separateScreens << "\n"
                               << "\toutputs:" << m_outputSettings;
 }
 
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h
index 9a160d2570..edb6906a4b 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.h
@@ -1,5 +1,6 @@
 /****************************************************************************
 **
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
@@ -65,6 +66,7 @@ public:
     bool supportsPBuffers() const Q_DECL_OVERRIDE;
 
     bool hwCursor() const;
+    bool separateScreens() const;
     QMap<QString, QVariantMap> outputSettings() const;
 
 private:
@@ -73,6 +75,7 @@ private:
     QEglFSKmsDevice *m_device;
     bool m_hwCursor;
     bool m_pbuffers;
+    bool m_separateScreens;
     QString m_devicePath;
     QMap<QString, QVariantMap> m_outputSettings;
 };
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp
index a27819d1dd..5e49c224a0 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp
@@ -1,5 +1,6 @@
 /****************************************************************************
 **
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
@@ -120,6 +121,7 @@ QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration,
     , m_cursor(Q_NULLPTR)
     , m_interruptHandler(new QEglFSKmsInterruptHandler(this))
 {
+    m_siblings << this;
 }
 
 QEglFSKmsScreen::~QEglFSKmsScreen()
@@ -185,6 +187,9 @@ QString QEglFSKmsScreen::name() const
 QPlatformCursor *QEglFSKmsScreen::cursor() const
 {
     if (m_integration->hwCursor()) {
+        if (!m_integration->separateScreens())
+            return m_device->globalCursor();
+
         if (m_cursor.isNull()) {
             QEglFSKmsScreen *that = const_cast<QEglFSKmsScreen *>(this);
             that->m_cursor.reset(new QEglFSKmsCursor(that));
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h
index 16521c7fe0..4c1b0d02ad 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h
@@ -1,5 +1,6 @@
 /****************************************************************************
 **
+** Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
 ** Copyright (C) 2015 The Qt Company Ltd.
 ** Contact: http://www.qt.io/licensing/
 **
@@ -85,6 +86,10 @@ public:
 
     qreal refreshRate() const Q_DECL_OVERRIDE;
 
+    QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_siblings; }
+    void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; }
+
+    QEglFSKmsIntegration *integration() const { return m_integration; }
     QEglFSKmsDevice *device() const { return m_device; }
 
     gbm_surface *surface() const { return m_gbm_surface; }
@@ -110,6 +115,8 @@ private:
     QPoint m_pos;
     QScopedPointer<QEglFSKmsCursor> m_cursor;
 
+    QList<QPlatformScreen *> m_siblings;
+
     struct FrameBuffer {
         FrameBuffer() : fb(0) {}
         uint32_t fb;