diff options
author | Louai Al-Khanji <louai.al-khanji@digia.com> | 2014-09-16 09:55:51 +0300 |
---|---|---|
committer | Louai Al-Khanji <louai.al-khanji@digia.com> | 2014-09-16 15:43:18 +0200 |
commit | fabef06f3326886bad9e6c039ca76e6368ad762c (patch) | |
tree | 5934eef89f432e2227d14341022d1cd26399d1cf /src/plugins/platforms/eglfs | |
parent | f14470fa0062a71b9eeac7f6904fbc5d92c133d2 (diff) |
EGLFS KMS Hooks: Add HW cursor support
Change-Id: I9cd62f3cbcc4ab844f05c43e775512b72d4ff159
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
Diffstat (limited to 'src/plugins/platforms/eglfs')
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfshooks_kms.cpp | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp b/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp index c63b8d0003..1e5b50e6ce 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_kms.cpp @@ -43,7 +43,12 @@ #include <QtPlatformSupport/private/qdevicediscovery_p.h> #include <QtCore/private/qcore_unix_p.h> #include <QtCore/QScopedPointer> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> #include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qpa/qplatformcursor.h> +#include <QtGui/QPainter> #include <xf86drm.h> #include <xf86drmMode.h> @@ -51,6 +56,43 @@ QT_USE_NAMESPACE +class QKmsCursor : public QPlatformCursor +{ + Q_OBJECT +public: + QKmsCursor(gbm_device *gbm_device, int dri_fd, uint32_t crtcId); + ~QKmsCursor(); + + // input methods + void pointerEvent(const QMouseEvent & event) Q_DECL_OVERRIDE; +#ifndef QT_NO_CURSOR + void changeCursor(QCursor * windowCursor, QWindow * window) Q_DECL_OVERRIDE; +#endif + QPoint pos() const Q_DECL_OVERRIDE; + void setPos(const QPoint &pos) Q_DECL_OVERRIDE; + +private: + void initCursorAtlas(); + + gbm_device *m_gbm_device; + int m_dri_fd; + uint32_t m_crtc; + gbm_bo *m_bo; + QPoint m_pos; + QPlatformCursorImage m_cursorImage; + bool m_visible; + + // cursor atlas information + struct CursorAtlas { + CursorAtlas() : cursorsPerRow(0), cursorWidth(0), cursorHeight(0) { } + int cursorsPerRow; + int width, height; // width and height of the atlas + int cursorWidth, cursorHeight; // width and height of cursors inside the atlas + QList<QPoint> hotSpots; + QImage image; + } m_cursorAtlas; +}; + class QEglKmsHooks : public QEglFSHooks { public: @@ -68,6 +110,7 @@ public: const QSurfaceFormat &format) Q_DECL_OVERRIDE; void destroyNativeWindow(EGLNativeWindowType window) Q_DECL_OVERRIDE; bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; + QPlatformCursor *createCursor(QPlatformScreen *screen) const Q_DECL_OVERRIDE; void presentBuffer() Q_DECL_OVERRIDE; bool setup_kms(); @@ -221,6 +264,12 @@ bool QEglKmsHooks::hasCapability(QPlatformIntegration::Capability cap) const } } +QPlatformCursor *QEglKmsHooks::createCursor(QPlatformScreen *screen) const +{ + Q_UNUSED(screen); + return new QKmsCursor(m_gbm_device, m_dri_fd, m_drm_crtc); +} + static void gbm_bo_destroyed_callback(gbm_bo *bo, void *data) { QEglKmsHooks::FrameBuffer *fb = static_cast<QEglKmsHooks::FrameBuffer *>(data); @@ -414,3 +463,143 @@ bool QEglKmsHooks::setup_kms() return true; } + +QKmsCursor::QKmsCursor(gbm_device *gbm_device, int dri_fd, uint32_t crtcId) + : m_gbm_device(gbm_device) + , m_dri_fd(dri_fd) + , m_crtc(crtcId) + , m_bo(gbm_bo_create(gbm_device, 64, 64, GBM_FORMAT_ARGB8888, + GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE)) + , m_cursorImage(0, 0, 0, 0, 0, 0) + , m_visible(true) +{ + if (!m_bo) { + qWarning("Could not create buffer for cursor!"); + } else { + initCursorAtlas(); + } + + drmModeMoveCursor(m_dri_fd, m_crtc, 0, 0); +} + +QKmsCursor::~QKmsCursor() +{ + drmModeSetCursor(m_dri_fd, m_crtc, 0, 0, 0); + drmModeMoveCursor(m_dri_fd, m_crtc, 0, 0); + + gbm_bo_destroy(m_bo); + m_bo = Q_NULLPTR; +} + +void QKmsCursor::pointerEvent(const QMouseEvent &event) +{ + setPos(event.screenPos().toPoint()); +} + +#ifndef QT_NO_CURSOR +void QKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) +{ + Q_UNUSED(window); + + if (!m_visible) + return; + + const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor; + if (newShape == Qt::BitmapCursor) { + m_cursorImage.set(windowCursor->pixmap().toImage(), + windowCursor->hotSpot().x(), + windowCursor->hotSpot().y()); + } else { + // Standard cursor, look up in atlas + const int width = m_cursorAtlas.cursorWidth; + const int height = m_cursorAtlas.cursorHeight; + const qreal ws = (qreal)m_cursorAtlas.cursorWidth / m_cursorAtlas.width; + const qreal hs = (qreal)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; + + QRect textureRect(ws * (newShape % m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.width, + hs * (newShape / m_cursorAtlas.cursorsPerRow) * m_cursorAtlas.height, + width, + height); + QPoint hotSpot = m_cursorAtlas.hotSpots[newShape]; + m_cursorImage.set(m_cursorAtlas.image.copy(textureRect), + hotSpot.x(), + hotSpot.y()); + } + + if (m_cursorImage.image()->width() > 64 || m_cursorImage.image()->height() > 64) + qWarning("Cursor larger than 64x64, cursor will be clipped."); + + QImage cursorImage(64, 64, QImage::Format_ARGB32); + cursorImage.fill(Qt::transparent); + + QPainter painter; + painter.begin(&cursorImage); + painter.drawImage(0, 0, *m_cursorImage.image()); + painter.end(); + + gbm_bo_write(m_bo, cursorImage.constBits(), cursorImage.byteCount()); + + uint32_t handle = gbm_bo_get_handle(m_bo).u32; + QPoint hot = m_cursorImage.hotspot(); + int status = drmModeSetCursor2(m_dri_fd, m_crtc, handle, 64, 64, hot.x(), hot.y()); + if (status != 0) + qWarning("Could not set cursor: %d", status); +} +#endif // QT_NO_CURSOR + +QPoint QKmsCursor::pos() const +{ + return m_pos; +} + +void QKmsCursor::setPos(const QPoint &pos) +{ + QPoint adjustedPos = pos - m_cursorImage.hotspot(); + int ret = drmModeMoveCursor(m_dri_fd, m_crtc, adjustedPos.x(), adjustedPos.y()); + if (ret == 0) { + m_pos = pos; + } else { + qWarning("Failed to move cursor: %d", ret); + } +} + +void QKmsCursor::initCursorAtlas() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR"); + if (json.isEmpty()) + json = ":/cursor.json"; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + drmModeSetCursor(m_dri_fd, m_crtc, 0, 0, 0); + drmModeMoveCursor(m_dri_fd, m_crtc, 0, 0); + m_visible = false; + return; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + QJsonObject object = doc.object(); + + QString atlas = object.value(QLatin1String("image")).toString(); + Q_ASSERT(!atlas.isEmpty()); + + const int cursorsPerRow = object.value(QLatin1String("cursorsPerRow")).toDouble(); + Q_ASSERT(cursorsPerRow); + m_cursorAtlas.cursorsPerRow = cursorsPerRow; + + const QJsonArray hotSpots = object.value(QLatin1String("hotSpots")).toArray(); + Q_ASSERT(hotSpots.count() == Qt::LastCursor + 1); + for (int i = 0; i < hotSpots.count(); i++) { + QPoint hotSpot(hotSpots[i].toArray()[0].toDouble(), hotSpots[i].toArray()[1].toDouble()); + m_cursorAtlas.hotSpots << hotSpot; + } + + QImage image = QImage(atlas).convertToFormat(QImage::Format_ARGB32); + m_cursorAtlas.cursorWidth = image.width() / m_cursorAtlas.cursorsPerRow; + m_cursorAtlas.cursorHeight = image.height() / ((Qt::LastCursor + cursorsPerRow) / cursorsPerRow); + m_cursorAtlas.width = image.width(); + m_cursorAtlas.height = image.height(); + m_cursorAtlas.image = image; +} + +#include "qeglfshooks_kms.moc" |