Drop Cursor::image()

Use CursorSource::image() instead.

Cursor caching in the ScreenCastStream has been changed so
QImage::cacheKey() is not being used. This is rather a preparation for
making kwin grab the contents of the cursor scene.
This commit is contained in:
Vlad Zahorodnii 2023-05-07 19:41:00 +03:00
parent af7c1db43b
commit 3d5b5844d0
6 changed files with 47 additions and 39 deletions

View file

@ -1090,7 +1090,7 @@ void PointerInputTest::testCursorImage()
input()->pointer()->warp(QPointF(800, 800)); input()->pointer()->warp(QPointF(800, 800));
auto p = input()->pointer(); auto p = input()->pointer();
// at the moment it should be the fallback cursor // at the moment it should be the fallback cursor
const QImage fallbackCursor = cursor->image(); const QImage fallbackCursor = kwinApp()->cursorImage().image();
QVERIFY(!fallbackCursor.isNull()); QVERIFY(!fallbackCursor.isNull());
// create a window // create a window
@ -1107,7 +1107,7 @@ void PointerInputTest::testCursorImage()
// move cursor to center of window, this should first set a null pointer, so we still show old cursor // move cursor to center of window, this should first set a null pointer, so we still show old cursor
input()->pointer()->warp(window->frameGeometry().center()); input()->pointer()->warp(window->frameGeometry().center());
QCOMPARE(p->focus(), window); QCOMPARE(p->focus(), window);
QCOMPARE(cursor->image(), fallbackCursor); QCOMPARE(kwinApp()->cursorImage().image(), fallbackCursor);
QVERIFY(enteredSpy.wait()); QVERIFY(enteredSpy.wait());
// create a cursor on the pointer // create a cursor on the pointer
@ -1121,13 +1121,13 @@ void PointerInputTest::testCursorImage()
cursorSurface->commit(); cursorSurface->commit();
pointer->setCursor(cursorSurface.get(), QPoint(5, 5)); pointer->setCursor(cursorSurface.get(), QPoint(5, 5));
QVERIFY(cursorRenderedSpy.wait()); QVERIFY(cursorRenderedSpy.wait());
QCOMPARE(cursor->image(), red); QCOMPARE(kwinApp()->cursorImage().image(), red);
QCOMPARE(cursor->hotspot(), QPoint(5, 5)); QCOMPARE(cursor->hotspot(), QPoint(5, 5));
// change hotspot // change hotspot
pointer->setCursor(cursorSurface.get(), QPoint(6, 6)); pointer->setCursor(cursorSurface.get(), QPoint(6, 6));
Test::flushWaylandConnection(); Test::flushWaylandConnection();
QTRY_COMPARE(cursor->hotspot(), QPoint(6, 6)); QTRY_COMPARE(cursor->hotspot(), QPoint(6, 6));
QCOMPARE(cursor->image(), red); QCOMPARE(kwinApp()->cursorImage().image(), red);
// change the buffer // change the buffer
QImage blue = QImage(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); QImage blue = QImage(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
@ -1137,7 +1137,7 @@ void PointerInputTest::testCursorImage()
cursorSurface->damage(QRect(0, 0, 10, 10)); cursorSurface->damage(QRect(0, 0, 10, 10));
cursorSurface->commit(); cursorSurface->commit();
QVERIFY(cursorRenderedSpy.wait()); QVERIFY(cursorRenderedSpy.wait());
QTRY_COMPARE(cursor->image(), blue); QTRY_COMPARE(kwinApp()->cursorImage().image(), blue);
QCOMPARE(cursor->hotspot(), QPoint(6, 6)); QCOMPARE(cursor->hotspot(), QPoint(6, 6));
// scaled cursor // scaled cursor
@ -1150,19 +1150,19 @@ void PointerInputTest::testCursorImage()
cursorSurface->damage(QRect(0, 0, 20, 20)); cursorSurface->damage(QRect(0, 0, 20, 20));
cursorSurface->commit(); cursorSurface->commit();
QVERIFY(cursorRenderedSpy.wait()); QVERIFY(cursorRenderedSpy.wait());
QTRY_COMPARE(cursor->image(), blueScaled); QTRY_COMPARE(kwinApp()->cursorImage().image(), blueScaled);
QCOMPARE(cursor->hotspot(), QPoint(6, 6)); // surface-local (so not changed) QCOMPARE(cursor->hotspot(), QPoint(6, 6)); // surface-local (so not changed)
// hide the cursor // hide the cursor
pointer->setCursor(nullptr); pointer->setCursor(nullptr);
Test::flushWaylandConnection(); Test::flushWaylandConnection();
QTRY_VERIFY(cursor->image().isNull()); QTRY_VERIFY(kwinApp()->cursorImage().image().isNull());
// move cursor somewhere else, should reset to fallback cursor // move cursor somewhere else, should reset to fallback cursor
input()->pointer()->warp(window->frameGeometry().bottomLeft() + QPoint(20, 20)); input()->pointer()->warp(window->frameGeometry().bottomLeft() + QPoint(20, 20));
QVERIFY(!p->focus()); QVERIFY(!p->focus());
QVERIFY(!cursor->image().isNull()); QVERIFY(!kwinApp()->cursorImage().image().isNull());
QCOMPARE(cursor->image(), fallbackCursor); QCOMPARE(kwinApp()->cursorImage().image(), fallbackCursor);
} }
class HelperEffect : public Effect class HelperEffect : public Effect
@ -1183,7 +1183,6 @@ void PointerInputTest::testEffectOverrideCursorImage()
// we need a pointer to get the enter event and set a cursor // we need a pointer to get the enter event and set a cursor
auto pointer = m_seat->createPointer(m_seat); auto pointer = m_seat->createPointer(m_seat);
auto cursor = Cursors::self()->mouse();
QVERIFY(pointer); QVERIFY(pointer);
QVERIFY(pointer->isValid()); QVERIFY(pointer->isValid());
QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered); QSignalSpy enteredSpy(pointer, &KWayland::Client::Pointer::entered);
@ -1191,7 +1190,7 @@ void PointerInputTest::testEffectOverrideCursorImage()
// move cursor somewhere the new window won't open // move cursor somewhere the new window won't open
input()->pointer()->warp(QPointF(800, 800)); input()->pointer()->warp(QPointF(800, 800));
// here we should have the fallback cursor // here we should have the fallback cursor
const QImage fallback = cursor->image(); const QImage fallback = kwinApp()->cursorImage().image();
QVERIFY(!fallback.isNull()); QVERIFY(!fallback.isNull());
// now let's create a window // now let's create a window
@ -1210,33 +1209,33 @@ void PointerInputTest::testEffectOverrideCursorImage()
input()->pointer()->warp(window->frameGeometry().center()); input()->pointer()->warp(window->frameGeometry().center());
QVERIFY(enteredSpy.wait()); QVERIFY(enteredSpy.wait());
// cursor image should still be fallback // cursor image should still be fallback
QCOMPARE(cursor->image(), fallback); QCOMPARE(kwinApp()->cursorImage().image(), fallback);
// now create an effect and set an override cursor // now create an effect and set an override cursor
std::unique_ptr<HelperEffect> effect(new HelperEffect); std::unique_ptr<HelperEffect> effect(new HelperEffect);
effects->startMouseInterception(effect.get(), Qt::SizeAllCursor); effects->startMouseInterception(effect.get(), Qt::SizeAllCursor);
const QImage sizeAll = cursor->image(); const QImage sizeAll = kwinApp()->cursorImage().image();
QVERIFY(!sizeAll.isNull()); QVERIFY(!sizeAll.isNull());
QVERIFY(sizeAll != fallback); QVERIFY(sizeAll != fallback);
QVERIFY(leftSpy.wait()); QVERIFY(leftSpy.wait());
// let's change to arrow cursor, this should be our fallback // let's change to arrow cursor, this should be our fallback
effects->defineCursor(Qt::ArrowCursor); effects->defineCursor(Qt::ArrowCursor);
QCOMPARE(cursor->image(), fallback); QCOMPARE(kwinApp()->cursorImage().image(), fallback);
// back to size all // back to size all
effects->defineCursor(Qt::SizeAllCursor); effects->defineCursor(Qt::SizeAllCursor);
QCOMPARE(cursor->image(), sizeAll); QCOMPARE(kwinApp()->cursorImage().image(), sizeAll);
// move cursor outside the window area // move cursor outside the window area
input()->pointer()->warp(QPointF(800, 800)); input()->pointer()->warp(QPointF(800, 800));
// and end the override, which should switch to fallback // and end the override, which should switch to fallback
effects->stopMouseInterception(effect.get()); effects->stopMouseInterception(effect.get());
QCOMPARE(cursor->image(), fallback); QCOMPARE(kwinApp()->cursorImage().image(), fallback);
// start mouse interception again // start mouse interception again
effects->startMouseInterception(effect.get(), Qt::SizeAllCursor); effects->startMouseInterception(effect.get(), Qt::SizeAllCursor);
QCOMPARE(cursor->image(), sizeAll); QCOMPARE(kwinApp()->cursorImage().image(), sizeAll);
// move cursor to area of window // move cursor to area of window
input()->pointer()->warp(window->frameGeometry().center()); input()->pointer()->warp(window->frameGeometry().center());
@ -1247,7 +1246,7 @@ void PointerInputTest::testEffectOverrideCursorImage()
// after ending the interception we should get an enter event // after ending the interception we should get an enter event
effects->stopMouseInterception(effect.get()); effects->stopMouseInterception(effect.get());
QVERIFY(enteredSpy.wait()); QVERIFY(enteredSpy.wait());
QVERIFY(cursor->image().isNull()); QVERIFY(kwinApp()->cursorImage().image().isNull());
} }
void PointerInputTest::testPopup() void PointerInputTest::testPopup()

View file

@ -175,14 +175,6 @@ bool Cursor::isOnOutput(Output *output) const
return geometry().intersects(output->geometry()); return geometry().intersects(output->geometry());
} }
QImage Cursor::image() const
{
if (Q_UNLIKELY(!m_source)) {
return QImage();
}
return m_source->image();
}
QPointF Cursor::hotspot() const QPointF Cursor::hotspot() const
{ {
if (Q_UNLIKELY(!m_source)) { if (Q_UNLIKELY(!m_source)) {

View file

@ -168,7 +168,6 @@ public:
*/ */
xcb_cursor_t x11Cursor(const QByteArray &name); xcb_cursor_t x11Cursor(const QByteArray &name);
QImage image() const;
QPointF hotspot() const; QPointF hotspot() const;
QRectF geometry() const; QRectF geometry() const;
QRectF rect() const; QRectF rect() const;

View file

@ -17,6 +17,7 @@
#include "core/outputbackend.h" #include "core/outputbackend.h"
#include "core/session.h" #include "core/session.h"
#include "cursor.h" #include "cursor.h"
#include "cursorsource.h"
#include "effects.h" #include "effects.h"
#include "input.h" #include "input.h"
#include "inputmethod.h" #include "inputmethod.h"
@ -641,7 +642,10 @@ ScreenLockerWatcher *Application::screenLockerWatcher() const
PlatformCursorImage Application::cursorImage() const PlatformCursorImage Application::cursorImage() const
{ {
Cursor *cursor = Cursors::self()->currentCursor(); Cursor *cursor = Cursors::self()->currentCursor();
return PlatformCursorImage(cursor->image(), cursor->hotspot()); if (CursorSource *source = cursor->source()) {
return PlatformCursorImage(source->image(), source->hotspot());
}
return PlatformCursorImage();
} }
void Application::startInteractiveWindowSelection(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName) void Application::startInteractiveWindowSelection(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName)

View file

@ -413,10 +413,12 @@ bool ScreenCastStream::createStream()
} }
if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded) { if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded) {
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &ScreenCastStream::invalidateCursor);
connect(Cursors::self(), &Cursors::positionChanged, this, [this] { connect(Cursors::self(), &Cursors::positionChanged, this, [this] {
recordFrame({}); recordFrame({});
}); });
} else if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Metadata) { } else if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Metadata) {
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &ScreenCastStream::invalidateCursor);
connect(Cursors::self(), &Cursors::positionChanged, this, &ScreenCastStream::recordCursor); connect(Cursors::self(), &Cursors::positionChanged, this, &ScreenCastStream::recordCursor);
} }
@ -524,7 +526,8 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
QImage dest(data, size.width(), size.height(), stride, hasAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888); QImage dest(data, size.width(), size.height(), stride, hasAlpha ? QImage::Format_RGBA8888_Premultiplied : QImage::Format_RGB888);
QPainter painter(&dest); QPainter painter(&dest);
const auto position = (cursor->pos() - m_cursor.viewport.topLeft() - cursor->hotspot()) * m_cursor.scale; const auto position = (cursor->pos() - m_cursor.viewport.topLeft() - cursor->hotspot()) * m_cursor.scale;
painter.drawImage(QRect{position.toPoint(), cursor->image().size()}, cursor->image()); const PlatformCursorImage cursorImage = kwinApp()->cursorImage();
painter.drawImage(QRect{position.toPoint(), cursorImage.image().size()}, cursorImage.image());
} }
} else { } else {
auto &buf = m_dmabufDataForPwBuffer[buffer]; auto &buf = m_dmabufDataForPwBuffer[buffer];
@ -542,7 +545,16 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
auto cursor = Cursors::self()->currentCursor(); auto cursor = Cursors::self()->currentCursor();
if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded && exclusiveContains(m_cursor.viewport, cursor->pos())) { if (m_cursor.mode == KWaylandServer::ScreencastV1Interface::Embedded && exclusiveContains(m_cursor.viewport, cursor->pos())) {
if (!cursor->image().isNull()) { if (m_cursor.invalid) {
m_cursor.invalid = false;
const PlatformCursorImage cursorImage = kwinApp()->cursorImage();
if (cursorImage.isNull()) {
m_cursor.texture = nullptr;
} else {
m_cursor.texture = std::make_unique<GLTexture>(cursorImage.image());
}
}
if (m_cursor.texture) {
GLFramebuffer::pushFramebuffer(buf->framebuffer()); GLFramebuffer::pushFramebuffer(buf->framebuffer());
QRect r(QPoint(), size); QRect r(QPoint(), size);
@ -552,13 +564,9 @@ void ScreenCastStream::recordFrame(const QRegion &_damagedRegion)
mvp.ortho(r); mvp.ortho(r);
shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp); shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
if (!m_cursor.texture || m_cursor.lastKey != cursor->image().cacheKey()) {
m_cursor.texture.reset(new GLTexture(cursor->image()));
}
m_cursor.texture->setContentTransform(TextureTransforms()); m_cursor.texture->setContentTransform(TextureTransforms());
const auto cursorRect = cursorGeometry(cursor); const auto cursorRect = cursorGeometry(cursor);
mvp.translate(cursorRect.left(), r.height() - cursorRect.top() - cursor->image().height()); mvp.translate(cursorRect.left(), r.height() - cursorRect.top() - m_cursor.texture->height());
shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp); shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -626,6 +634,11 @@ void ScreenCastStream::addDamage(spa_buffer *spaBuffer, const QRegion &damagedRe
} }
} }
void ScreenCastStream::invalidateCursor()
{
m_cursor.invalid = true;
}
void ScreenCastStream::recordCursor() void ScreenCastStream::recordCursor()
{ {
Q_ASSERT(!m_stopped); Q_ASSERT(!m_stopped);
@ -815,12 +828,11 @@ void ScreenCastStream::sendCursorData(Cursor *cursor, spa_meta_cursor *spa_meta_
spa_meta_cursor->hotspot.y = cursor->hotspot().y() * m_cursor.scale; spa_meta_cursor->hotspot.y = cursor->hotspot().y() * m_cursor.scale;
spa_meta_cursor->bitmap_offset = 0; spa_meta_cursor->bitmap_offset = 0;
const QImage image = cursor->image(); if (!m_cursor.invalid) {
if (image.cacheKey() == m_cursor.lastKey) {
return; return;
} }
m_cursor.lastKey = image.cacheKey(); m_cursor.invalid = false;
spa_meta_cursor->bitmap_offset = sizeof(struct spa_meta_cursor); spa_meta_cursor->bitmap_offset = sizeof(struct spa_meta_cursor);
const QSize targetSize = (cursor->rect().size() * m_cursor.scale).toSize(); const QSize targetSize = (cursor->rect().size() * m_cursor.scale).toSize();
@ -842,6 +854,7 @@ void ScreenCastStream::sendCursorData(Cursor *cursor, spa_meta_cursor *spa_meta_
QImage::Format_RGBA8888_Premultiplied); QImage::Format_RGBA8888_Premultiplied);
dest.fill(Qt::transparent); dest.fill(Qt::transparent);
const QImage image = kwinApp()->cursorImage().image();
if (!image.isNull()) { if (!image.isNull()) {
QPainter painter(&dest); QPainter painter(&dest);
painter.drawImage(QRect({0, 0}, targetSize), image); painter.drawImage(QRect({0, 0}, targetSize), image);

View file

@ -65,6 +65,7 @@ public:
void setCursorMode(KWaylandServer::ScreencastV1Interface::CursorMode mode, qreal scale, const QRectF &viewport); void setCursorMode(KWaylandServer::ScreencastV1Interface::CursorMode mode, qreal scale, const QRectF &viewport);
public Q_SLOTS: public Q_SLOTS:
void invalidateCursor();
void recordCursor(); void recordCursor();
Q_SIGNALS: Q_SIGNALS:
@ -117,10 +118,10 @@ private:
const QSize bitmapSize = QSize(256, 256); const QSize bitmapSize = QSize(256, 256);
qreal scale = 1; qreal scale = 1;
QRectF viewport; QRectF viewport;
qint64 lastKey = 0;
QRectF lastRect; QRectF lastRect;
std::unique_ptr<GLTexture> texture; std::unique_ptr<GLTexture> texture;
bool visible = false; bool visible = false;
bool invalid = true;
} m_cursor; } m_cursor;
QRectF cursorGeometry(Cursor *cursor) const; QRectF cursorGeometry(Cursor *cursor) const;