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.
This commit is contained in:
Martin Gräßlin 2015-04-13 16:25:34 +02:00
parent 877c33fe7d
commit 38b676d809
4 changed files with 122 additions and 5 deletions

View file

@ -56,6 +56,12 @@ void AbstractBackend::installCursorFromServer()
if (!m_softWareCursor) { if (!m_softWareCursor) {
return; return;
} }
triggerCursorRepaint();
updateCursorFromServer();
}
void AbstractBackend::updateCursorFromServer()
{
if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) { if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) {
return; return;
} }
@ -71,9 +77,9 @@ void AbstractBackend::installCursorFromServer()
if (!buffer) { if (!buffer) {
return; return;
} }
triggerCursorRepaint();
m_cursor.hotspot = c->hotspot(); m_cursor.hotspot = c->hotspot();
m_cursor.image = buffer->data().copy(); m_cursor.image = buffer->data().copy();
emit cursorChanged();
} }
void AbstractBackend::installCursorImage(Qt::CursorShape shape) void AbstractBackend::installCursorImage(Qt::CursorShape shape)
@ -81,6 +87,11 @@ void AbstractBackend::installCursorImage(Qt::CursorShape shape)
if (!m_softWareCursor) { if (!m_softWareCursor) {
return; return;
} }
updateCursorImage(shape);
}
void AbstractBackend::updateCursorImage(Qt::CursorShape shape)
{
#if HAVE_WAYLAND_CURSOR #if HAVE_WAYLAND_CURSOR
if (!m_cursorTheme) { if (!m_cursorTheme) {
// check whether we can create it // check whether we can create it
@ -116,9 +127,12 @@ void AbstractBackend::installThemeCursor(quint32 id, const QPoint &hotspot)
if (!buffer) { if (!buffer) {
return; return;
} }
if (m_softWareCursor) {
triggerCursorRepaint(); triggerCursorRepaint();
}
m_cursor.hotspot = hotspot; m_cursor.hotspot = hotspot;
m_cursor.image = buffer->data().copy(); m_cursor.image = buffer->data().copy();
emit cursorChanged();
} }
Screens *AbstractBackend::createScreens(QObject *parent) Screens *AbstractBackend::createScreens(QObject *parent)

View file

@ -58,9 +58,14 @@ public:
} }
void markCursorAsRendered(); void markCursorAsRendered();
Q_SIGNALS:
void cursorChanged();
protected: protected:
explicit AbstractBackend(QObject *parent = nullptr); explicit AbstractBackend(QObject *parent = nullptr);
void setSoftWareCursor(bool set); void setSoftWareCursor(bool set);
void updateCursorFromServer();
void updateCursorImage(Qt::CursorShape shape);
private Q_SLOTS: private Q_SLOTS:
void installThemeCursor(quint32 id, const QPoint &hotspot); void installThemeCursor(quint32 id, const QPoint &hotspot);

View file

@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/ *********************************************************************/
#include "drm_backend.h" #include "drm_backend.h"
#include "composite.h" #include "composite.h"
#include "cursor.h"
#include "logind.h" #include "logind.h"
#include "scene_qpainter.h" #include "scene_qpainter.h"
#include "screens_drm.h" #include "screens_drm.h"
@ -30,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif #endif
// Qt // Qt
#include <QSocketNotifier> #include <QSocketNotifier>
#include <QPainter>
// system // system
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -41,6 +43,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <gbm.h> #include <gbm.h>
#endif #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 <QDebug> #include <QDebug>
namespace KWin namespace KWin
@ -50,11 +60,16 @@ DrmBackend::DrmBackend(QObject *parent)
: AbstractBackend(parent) : AbstractBackend(parent)
, m_udev(new Udev) , m_udev(new Udev)
{ {
m_cursor[0] = nullptr;
m_cursor[1] = nullptr;
} }
DrmBackend::~DrmBackend() DrmBackend::~DrmBackend()
{ {
if (m_fd >= 0) { if (m_fd >= 0) {
hideCursor();
delete m_cursor[0];
delete m_cursor[1];
close(m_fd); close(m_fd);
} }
} }
@ -122,6 +137,8 @@ void DrmBackend::openDrm()
m_drmId = device->sysNum(); m_drmId = device->sysNum();
queryResources(); queryResources();
emit screensQueried(); emit screensQueried();
initCursor();
} }
template <typename Pointer, void (*cleanupFunc)(Pointer*)> template <typename Pointer, void (*cleanupFunc)(Pointer*)>
@ -188,6 +205,78 @@ void DrmBackend::present(DrmBuffer *buffer)
drmModePageFlip(m_fd, m_crtcId, buffer->m_bufferId, DRM_MODE_PAGE_FLIP_EVENT, 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) Screens *DrmBackend::createScreens(QObject *parent)
{ {
return new DrmScreens(this, parent); return new DrmScreens(this, parent);
@ -286,7 +375,7 @@ DrmBuffer::~DrmBuffer()
#endif #endif
} }
bool DrmBuffer::map() bool DrmBuffer::map(QImage::Format format)
{ {
if (!m_handle || !m_bufferId) { if (!m_handle || !m_bufferId) {
return false; return false;
@ -302,7 +391,7 @@ bool DrmBuffer::map()
return false; return false;
} }
m_memory = address; 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(); return !m_image->isNull();
} }

View file

@ -45,6 +45,8 @@ public:
Screens *createScreens(QObject *parent = nullptr) override; Screens *createScreens(QObject *parent = nullptr) override;
QPainterBackend *createQPainterBackend() override; QPainterBackend *createQPainterBackend() override;
OpenGLBackend* createOpenGLBackend() override; OpenGLBackend* createOpenGLBackend() override;
void installCursorFromServer() override;
void installCursorImage(Qt::CursorShape shape) override;
void init(); void init();
DrmBuffer *createBuffer(const QSize &size); 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); static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data);
void openDrm(); void openDrm();
void queryResources(); void queryResources();
void setCursor();
void updateCursor();
void hideCursor();
void moveCursor();
void initCursor();
QScopedPointer<Udev> m_udev; QScopedPointer<Udev> m_udev;
int m_fd = -1; int m_fd = -1;
int m_drmId = 0; int m_drmId = 0;
@ -75,6 +82,8 @@ private:
quint32 m_connector = 0; quint32 m_connector = 0;
drmModeModeInfo m_mode; drmModeModeInfo m_mode;
bool m_pageFlipPending = false; bool m_pageFlipPending = false;
DrmBuffer *m_cursor[2];
int m_cursorIndex = 0;
}; };
class DrmBuffer class DrmBuffer
@ -82,7 +91,7 @@ class DrmBuffer
public: public:
~DrmBuffer(); ~DrmBuffer();
bool map(); bool map(QImage::Format format = QImage::Format_RGB32);
QImage *image() const { QImage *image() const {
return m_image; return m_image;
} }