[wayland] Add support for a "software" cursor

At least the framebuffer backend does not have support for an overlay
cursor. Thus the cursor needs to be rendered by the scene. This change
allows a backend to set that it needs a software cursor which triggers
tracking in the AbstractBackend. A repaint for the old cursor region is
triggered whenever the cursor pos changes.

So far only the QPainter/framebuffer scene is adjusted to render the
software cursor. This is done after rendering a frame with the up to
date cursor position.

There is one problem, though: the KWin internal cursors don't work
as we need to get it from the theme. Using wayland-cursor doesn't help
as it gives us a (client) wl_buffer* and we cannot read the memory back.
This commit is contained in:
Martin Gräßlin 2015-04-01 15:36:40 +02:00
parent de3788c094
commit c5693270db
5 changed files with 102 additions and 0 deletions

View file

@ -18,7 +18,13 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "abstract_backend.h"
#include "composite.h"
#include "cursor.h"
#include "wayland_server.h"
// KWayland
#include <KWayland/Server/buffer_interface.h>
#include <KWayland/Server/seat_interface.h>
#include <KWayland/Server/surface_interface.h>
namespace KWin
{
@ -36,6 +42,27 @@ AbstractBackend::~AbstractBackend()
void AbstractBackend::installCursorFromServer()
{
if (!m_softWareCursor) {
return;
}
if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) {
return;
}
auto c = waylandServer()->seat()->focusedPointer()->cursor();
if (!c) {
return;
}
auto cursorSurface = c->surface();
if (cursorSurface.isNull()) {
return;
}
auto buffer = cursorSurface.data()->buffer();
if (!buffer) {
return;
}
triggerCursorRepaint();
m_cursor.hotspot = c->hotspot();
m_cursor.image = buffer->data().copy();
}
void AbstractBackend::installCursorImage(Qt::CursorShape shape)
@ -59,4 +86,32 @@ QPainterBackend *AbstractBackend::createQPainterBackend()
return nullptr;
}
void AbstractBackend::setSoftWareCursor(bool set)
{
if (m_softWareCursor == set) {
return;
}
m_softWareCursor = set;
if (m_softWareCursor) {
connect(Cursor::self(), &Cursor::posChanged, this, &AbstractBackend::triggerCursorRepaint);
} else {
disconnect(Cursor::self(), &Cursor::posChanged, this, &AbstractBackend::triggerCursorRepaint);
}
}
void AbstractBackend::triggerCursorRepaint()
{
if (!Compositor::self() || m_cursor.image.isNull()) {
return;
}
Compositor::self()->addRepaint(m_cursor.lastRenderedPosition.x() - m_cursor.hotspot.x(),
m_cursor.lastRenderedPosition.y() - m_cursor.hotspot.y(),
m_cursor.image.width(), m_cursor.image.height());
}
void AbstractBackend::markCursorAsRendered()
{
m_cursor.lastRenderedPosition = Cursor::pos();
}
}

View file

@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef KWIN_ABSTRACT_BACKEND_H
#define KWIN_ABSTRACT_BACKEND_H
#include <kwin_export.h>
#include <QImage>
#include <QObject>
namespace KWin
@ -41,8 +42,29 @@ public:
virtual OpenGLBackend *createOpenGLBackend();
virtual QPainterBackend *createQPainterBackend();
bool usesSoftwareCursor() const {
return m_softWareCursor;
}
QImage softwareCursor() const {
return m_cursor.image;
}
QPoint softwareCursorHotspot() const {
return m_cursor.hotspot;
}
void markCursorAsRendered();
protected:
explicit AbstractBackend(QObject *parent = nullptr);
void setSoftWareCursor(bool set);
private:
void triggerCursorRepaint();
bool m_softWareCursor = false;
struct {
QPoint hotspot;
QImage image;
QPoint lastRenderedPosition;
} m_cursor;
};
}

View file

@ -39,6 +39,7 @@ namespace KWin
FramebufferBackend::FramebufferBackend(QObject *parent)
: AbstractBackend(parent)
{
setSoftWareCursor(true);
}
FramebufferBackend::~FramebufferBackend()

View file

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// KWin
#include "client.h"
#include "composite.h"
#include "cursor.h"
#include "deleted.h"
#include "effects.h"
#include "main.h"
@ -79,6 +80,11 @@ void QPainterBackend::setFailed(const QString &reason)
m_failed = true;
}
void QPainterBackend::renderCursor(QPainter *painter)
{
Q_UNUSED(painter)
}
#if HAVE_WAYLAND
//****************************************
// WaylandQPainterBackend
@ -311,6 +317,21 @@ bool FramebufferQPainterBackend::usesOverlayWindow() const
return false;
}
void FramebufferQPainterBackend::renderCursor(QPainter *painter)
{
if (!m_backend->usesSoftwareCursor()) {
return;
}
const QImage img = m_backend->softwareCursor();
if (img.isNull()) {
return;
}
const QPoint cursorPos = Cursor::pos();
const QPoint hotspot = m_backend->softwareCursorHotspot();
painter->drawImage(cursorPos - hotspot, img);
m_backend->markCursorAsRendered();
}
#endif
//****************************************
@ -380,6 +401,7 @@ qint64 SceneQPainter::paint(QRegion damage, ToplevelList toplevels)
}
QRegion updateRegion, validRegion;
paintScreen(&mask, damage, QRegion(), &updateRegion, &validRegion);
m_backend->renderCursor(m_painter.data());
m_backend->showOverlay();

View file

@ -93,6 +93,7 @@ public:
virtual QImage *buffer() = 0;
virtual bool needsFullRepaint() const = 0;
virtual void renderCursor(QPainter *painter);
protected:
QPainterBackend();
@ -166,6 +167,7 @@ public:
bool usesOverlayWindow() const override;
void prepareRenderingFrame() override;
void present(int mask, const QRegion &damage) override;
void renderCursor(QPainter *painter) override;
private:
QImage m_renderBuffer;