diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -160,6 +160,7 @@ void updateCursor(); void moveCursor(); void initCursor(); + void connectCursor(); void readOutputsConfiguration(); void writeOutputsConfiguration(); QByteArray generateOutputConfigurationUuid() const; diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -348,6 +348,7 @@ initCursor(); updateOutputs(); + connectCursor(); if (m_outputs.isEmpty()) { qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now"; @@ -605,17 +606,8 @@ return false; } -void DrmBackend::initCursor() +void DrmBackend::connectCursor() { - -#if HAVE_EGL_STREAMS - // Hardware cursors aren't currently supported with EGLStream backend, - // possibly an NVIDIA driver bug - if (m_useEglStreams) { - setSoftWareCursor(true); - } -#endif - m_cursorEnabled = waylandServer()->seat()->hasPointer(); connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::hasPointerChanged, this, [this] { @@ -634,6 +626,23 @@ } } ); + + // 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); +} + +void DrmBackend::initCursor() +{ + +#if HAVE_EGL_STREAMS + // Hardware cursors aren't currently supported with EGLStream backend, + // possibly an NVIDIA driver bug + if (m_useEglStreams) { + setSoftWareCursor(true); + } +#endif + uint64_t capability = 0; QSize cursorSize; if (drmGetCap(m_fd, DRM_CAP_CURSOR_WIDTH, &capability) == 0) { @@ -647,9 +656,6 @@ cursorSize.setHeight(64); } m_cursorSize = cursorSize; - // 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); } void DrmBackend::setCursor() diff --git a/plugins/platforms/drm/drm_buffer.h b/plugins/platforms/drm/drm_buffer.h --- a/plugins/platforms/drm/drm_buffer.h +++ b/plugins/platforms/drm/drm_buffer.h @@ -57,7 +57,7 @@ class DrmDumbBuffer : public DrmBuffer { public: - DrmDumbBuffer(int fd, const QSize &size); + DrmDumbBuffer(int fd, const QSize &size, QImage::Format format = QImage::Format_RGB32); ~DrmDumbBuffer() override; bool needsModeChange(DrmBuffer *b) const override; diff --git a/plugins/platforms/drm/drm_buffer.cpp b/plugins/platforms/drm/drm_buffer.cpp --- a/plugins/platforms/drm/drm_buffer.cpp +++ b/plugins/platforms/drm/drm_buffer.cpp @@ -32,15 +32,35 @@ namespace KWin { +#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ + ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) + +#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ + DrmBuffer:: DrmBuffer(int fd) : m_fd(fd) { } // DrmDumbBuffer -DrmDumbBuffer::DrmDumbBuffer(int fd, const QSize &size) +DrmDumbBuffer::DrmDumbBuffer(int fd, const QSize &size, QImage::Format format) : DrmBuffer(fd) { + uint32_t handles[4] = {}; + uint32_t strides[4] = {}; + uint32_t offsets[4] = {}; + uint32_t drm_format; + + switch (format) { + case QImage::Format_ARGB32: + drm_format = DRM_FORMAT_ARGB8888; + break; + default: + drm_format = DRM_FORMAT_XRGB8888; + break; + } + m_size = size; drm_mode_create_dumb createArgs; memset(&createArgs, 0, sizeof createArgs); @@ -51,9 +71,15 @@ qCWarning(KWIN_DRM) << "DRM_IOCTL_MODE_CREATE_DUMB failed"; return; } - m_handle = createArgs.handle; + m_handle = handles[0] = createArgs.handle; + m_stride = strides[0] = createArgs.pitch;; m_bufferSize = createArgs.size; - m_stride = createArgs.pitch; + if (drmModeAddFB2(fd, size.width(), size.height(), drm_format, + handles, strides, offsets, &m_bufferId, 0) != 0) { + qCWarning(KWIN_DRM) << "drmModeAddFB failed with errno" << errno; + return; + } + // Fallback to legacy API if (drmModeAddFB(fd, size.width(), size.height(), 24, 32, m_stride, createArgs.handle, &m_bufferId) != 0) { qCWarning(KWIN_DRM) << "drmModeAddFB failed with errno" << errno; diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -51,7 +51,6 @@ ///queues deleting the output after a page flip has completed. void teardown(); void releaseGbm(); - bool showCursor(DrmDumbBuffer *buffer); bool showCursor(); bool hideCursor(); void updateCursor(); @@ -96,6 +95,7 @@ Real }; bool doAtomicCommit(AtomicCommitMode mode); + bool atomicCursorCommit(DrmDumbBuffer *buffer); bool presentLegacy(DrmBuffer *buffer); bool setModeLegacy(DrmBuffer *buffer); @@ -107,6 +107,7 @@ void initUuid(); bool initPrimaryPlane(); bool initCursorPlane(); + bool showCursor(DrmDumbBuffer *buffer); void atomicEnable(); void atomicDisable(); @@ -158,6 +159,8 @@ } m_lastWorkingState; DrmDumbBuffer *m_cursor[2] = {nullptr, nullptr}; int m_cursorIndex = 0; + int m_cursorX = 0; + int m_cursorY = 0; bool m_hasNewCursor = false; bool m_deleted = false; }; diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -104,13 +104,18 @@ bool DrmOutput::hideCursor() { - return drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0) == 0; + if (!m_cursorPlane) + return drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0) == 0; + return atomicCursorCommit(nullptr); } bool DrmOutput::showCursor(DrmDumbBuffer *c) { - const QSize &s = c->size(); - return drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0; + if (!m_cursorPlane) { + const QSize &s = c->size(); + return drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0; + } + return atomicCursorCommit(c); } bool DrmOutput::showCursor() @@ -198,7 +203,15 @@ } p *= scale(); p -= hotspotMatrix.map(m_backend->softwareCursorHotspot()); - drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); + + if (!m_cursorPlane) { + drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); + return; + } + + m_cursorX = p.x(); + m_cursorY = p.y(); + atomicCursorCommit(m_cursor[m_cursorIndex]); } static QHash s_connectorNames = { @@ -252,6 +265,7 @@ if (!initPrimaryPlane()) { return false; } + initCursorPlane(); } else if (!m_crtc->blank()) { return false; } @@ -1028,6 +1042,51 @@ return true; } +bool DrmOutput::atomicCursorCommit(DrmDumbBuffer *c) +{ + drmModeAtomicReq *req = drmModeAtomicAlloc(); + int x, y, w, h, crtc_id, fb_id; + + if (c) { + const QSize &s = c->size(); + + w = s.width(); + h = s.height(); + x = m_cursorX; + y = m_cursorY; + crtc_id = m_crtc->id(); + fb_id = c->bufferId(); + } else { + w = h = 0; + x = y = 0; + crtc_id = 0; + fb_id = 0; + } + + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), w << 16); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), h << 16); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::CrtcX), x); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::CrtcY), y); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), w); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), h); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), crtc_id); + m_cursorPlane->setValue(int(DrmPlane::PropertyIndex::FbId), fb_id); + + bool ret = m_cursorPlane->atomicPopulate(req); + if (!ret) { + qCWarning(KWIN_DRM) << "Failed to populate cursor atomic planes. Abort atomic commit!"; + return false; + } + + if (drmModeAtomicCommit(m_backend->fd(), req, 0, this)) { + qCWarning(KWIN_DRM) << "Atomic request failed to commit cursor:" << strerror(errno); + return false; + } + return true; +} + bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) { if (enable) {