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:
parent
877c33fe7d
commit
38b676d809
4 changed files with 122 additions and 5 deletions
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue