From 38b676d8096d3a4e9ee8fb4e74c6a3fe86e8762a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 13 Apr 2015 16:25:34 +0200 Subject: [PATCH] Add support for hardware cursors in DrmBackend Creates two buffers with the size queried through the drm capabilities. The actual cursor image is retrieved using the software cursor functionality from the AbstractBackend and rendered into the shared memory buffer. The the buffer is set as the new cursor image and the rendering buffer for the cursor is swapped. The position is updated whenever the cursor image changes or the mouse position changes. --- abstract_backend.cpp | 18 ++++++++- abstract_backend.h | 5 +++ drm_backend.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++- drm_backend.h | 11 +++++- 4 files changed, 122 insertions(+), 5 deletions(-) diff --git a/abstract_backend.cpp b/abstract_backend.cpp index a38be0e97e..7b062fbe83 100644 --- a/abstract_backend.cpp +++ b/abstract_backend.cpp @@ -56,6 +56,12 @@ void AbstractBackend::installCursorFromServer() if (!m_softWareCursor) { return; } + triggerCursorRepaint(); + updateCursorFromServer(); +} + +void AbstractBackend::updateCursorFromServer() +{ if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) { return; } @@ -71,9 +77,9 @@ void AbstractBackend::installCursorFromServer() if (!buffer) { return; } - triggerCursorRepaint(); m_cursor.hotspot = c->hotspot(); m_cursor.image = buffer->data().copy(); + emit cursorChanged(); } void AbstractBackend::installCursorImage(Qt::CursorShape shape) @@ -81,6 +87,11 @@ void AbstractBackend::installCursorImage(Qt::CursorShape shape) if (!m_softWareCursor) { return; } + updateCursorImage(shape); +} + +void AbstractBackend::updateCursorImage(Qt::CursorShape shape) +{ #if HAVE_WAYLAND_CURSOR if (!m_cursorTheme) { // check whether we can create it @@ -116,9 +127,12 @@ void AbstractBackend::installThemeCursor(quint32 id, const QPoint &hotspot) if (!buffer) { return; } - triggerCursorRepaint(); + if (m_softWareCursor) { + triggerCursorRepaint(); + } m_cursor.hotspot = hotspot; m_cursor.image = buffer->data().copy(); + emit cursorChanged(); } Screens *AbstractBackend::createScreens(QObject *parent) diff --git a/abstract_backend.h b/abstract_backend.h index adfa8f757b..fdbcb81887 100644 --- a/abstract_backend.h +++ b/abstract_backend.h @@ -58,9 +58,14 @@ public: } void markCursorAsRendered(); +Q_SIGNALS: + void cursorChanged(); + protected: explicit AbstractBackend(QObject *parent = nullptr); void setSoftWareCursor(bool set); + void updateCursorFromServer(); + void updateCursorImage(Qt::CursorShape shape); private Q_SLOTS: void installThemeCursor(quint32 id, const QPoint &hotspot); diff --git a/drm_backend.cpp b/drm_backend.cpp index 928d3bd6db..e70476d9b8 100644 --- a/drm_backend.cpp +++ b/drm_backend.cpp @@ -19,6 +19,7 @@ along with this program. If not, see . *********************************************************************/ #include "drm_backend.h" #include "composite.h" +#include "cursor.h" #include "logind.h" #include "scene_qpainter.h" #include "screens_drm.h" @@ -30,6 +31,7 @@ along with this program. If not, see . #endif // Qt #include +#include // system #include #include @@ -41,6 +43,14 @@ along with this program. If not, see . #include #endif +#ifndef DRM_CAP_CURSOR_WIDTH +#define DRM_CAP_CURSOR_WIDTH 0x8 +#endif + +#ifndef DRM_CAP_CURSOR_HEIGHT +#define DRM_CAP_CURSOR_HEIGHT 0x9 +#endif + #include namespace KWin @@ -50,11 +60,16 @@ DrmBackend::DrmBackend(QObject *parent) : AbstractBackend(parent) , m_udev(new Udev) { + m_cursor[0] = nullptr; + m_cursor[1] = nullptr; } DrmBackend::~DrmBackend() { if (m_fd >= 0) { + hideCursor(); + delete m_cursor[0]; + delete m_cursor[1]; close(m_fd); } } @@ -122,6 +137,8 @@ void DrmBackend::openDrm() m_drmId = device->sysNum(); queryResources(); emit screensQueried(); + + initCursor(); } template @@ -188,6 +205,78 @@ void DrmBackend::present(DrmBuffer *buffer) drmModePageFlip(m_fd, m_crtcId, buffer->m_bufferId, DRM_MODE_PAGE_FLIP_EVENT, buffer); } +void DrmBackend::installCursorFromServer() +{ + updateCursorFromServer(); +} + +void DrmBackend::installCursorImage(Qt::CursorShape shape) +{ + updateCursorImage(shape); +} + +void DrmBackend::initCursor() +{ + uint64_t capability = 0; + QSize cursorSize; + if (drmGetCap(m_fd, DRM_CAP_CURSOR_WIDTH, &capability) == 0) { + cursorSize.setWidth(capability); + } else { + cursorSize.setWidth(64); + } + if (drmGetCap(m_fd, DRM_CAP_CURSOR_HEIGHT, &capability) == 0) { + cursorSize.setHeight(capability); + } else { + cursorSize.setHeight(64); + } + m_cursor[0] = createBuffer(cursorSize); + m_cursor[0]->map(QImage::Format_ARGB32_Premultiplied); + m_cursor[0]->image()->fill(Qt::transparent); + m_cursor[1] = createBuffer(cursorSize); + m_cursor[1]->map(QImage::Format_ARGB32_Premultiplied); + m_cursor[0]->image()->fill(Qt::transparent); + // now we have screens and can set cursors, so start tracking + connect(this, &DrmBackend::cursorChanged, this, &DrmBackend::updateCursor); + connect(Cursor::self(), &Cursor::posChanged, this, &DrmBackend::moveCursor); + installCursorImage(Qt::ArrowCursor); +} + +void DrmBackend::setCursor() +{ + DrmBuffer *c = m_cursor[m_cursorIndex]; + m_cursorIndex = (m_cursorIndex + 1) % 2; + drmModeSetCursor(m_fd, m_crtcId, c->m_handle, c->m_size.width(), c->m_size.height()); +} + +void DrmBackend::updateCursor() +{ + const QImage &cursorImage = softwareCursor(); + if (cursorImage.isNull()) { + hideCursor(); + return; + } + QImage *c = m_cursor[m_cursorIndex]->image(); + c->fill(Qt::transparent); + QPainter p; + p.begin(c); + p.drawImage(QPoint(0, 0), cursorImage); + p.end(); + + setCursor(); + moveCursor(); +} + +void DrmBackend::hideCursor() +{ + drmModeSetCursor(m_fd, m_crtcId, 0, 0, 0); +} + +void DrmBackend::moveCursor() +{ + const QPoint p = Cursor::pos() - softwareCursorHotspot(); + drmModeMoveCursor(m_fd, m_crtcId, p.x(), p.y()); +} + Screens *DrmBackend::createScreens(QObject *parent) { return new DrmScreens(this, parent); @@ -286,7 +375,7 @@ DrmBuffer::~DrmBuffer() #endif } -bool DrmBuffer::map() +bool DrmBuffer::map(QImage::Format format) { if (!m_handle || !m_bufferId) { return false; @@ -302,7 +391,7 @@ bool DrmBuffer::map() return false; } m_memory = address; - m_image = new QImage((uchar*)m_memory, m_size.width(), m_size.height(), m_stride, QImage::Format_RGB32); + m_image = new QImage((uchar*)m_memory, m_size.width(), m_size.height(), m_stride, format); return !m_image->isNull(); } diff --git a/drm_backend.h b/drm_backend.h index 92fa769214..ef36898bda 100644 --- a/drm_backend.h +++ b/drm_backend.h @@ -45,6 +45,8 @@ public: Screens *createScreens(QObject *parent = nullptr) override; QPainterBackend *createQPainterBackend() override; OpenGLBackend* createOpenGLBackend() override; + void installCursorFromServer() override; + void installCursorImage(Qt::CursorShape shape) override; void init(); DrmBuffer *createBuffer(const QSize &size); @@ -66,6 +68,11 @@ private: static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data); void openDrm(); void queryResources(); + void setCursor(); + void updateCursor(); + void hideCursor(); + void moveCursor(); + void initCursor(); QScopedPointer m_udev; int m_fd = -1; int m_drmId = 0; @@ -75,6 +82,8 @@ private: quint32 m_connector = 0; drmModeModeInfo m_mode; bool m_pageFlipPending = false; + DrmBuffer *m_cursor[2]; + int m_cursorIndex = 0; }; class DrmBuffer @@ -82,7 +91,7 @@ class DrmBuffer public: ~DrmBuffer(); - bool map(); + bool map(QImage::Format format = QImage::Format_RGB32); QImage *image() const { return m_image; }