kwin/src/wayland/autotests/server/test_viewporter_interface.cpp
Vlad Zahorodnii 45812785cf Introduce surface-to-buffer and buffer-to-surface matrices
The compositor needs to monitor changes in the mapping between the
surface local coordinates and the buffer coordinates because texture
coordinates correspond to the latter. One way to do it is to monitor
things such as the surface size, the buffer size, the buffer scale,
etc. The main problem with doing so is that there are so many factors
that contribute to how mapping between the surface local coordinate
space and the buffer coordinate space is performed.

In order to provide a generic way for monitoring changes in the mapping
between the surface local coordinate space and the buffer coordinate
space, this patch introduces two new matrices. The first one specifies
how the surface-local coordinates are mapped to buffer coordinates, and
the other one specifies how to map the buffer coordinates to surface
local coordinates.

With the new two matrices, the compositor has a generic way to get
notified when it has to re-compute texture coordinates.
2020-06-19 06:43:31 +00:00

200 lines
7.1 KiB
C++

/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include <QThread>
#include <QtTest>
#include "../../src/server/compositor_interface.h"
#include "../../src/server/display.h"
#include "../../src/server/surface_interface.h"
#include "../../src/server/viewporter_interface.h"
#include "KWayland/Client/compositor.h"
#include "KWayland/Client/connection_thread.h"
#include "KWayland/Client/event_queue.h"
#include "KWayland/Client/registry.h"
#include "KWayland/Client/shm_pool.h"
#include "KWayland/Client/surface.h"
#include "qwayland-viewporter.h"
using namespace KWaylandServer;
class Viewporter : public QtWayland::wp_viewporter
{
};
class Viewport : public QtWayland::wp_viewport
{
};
class TestViewporterInterface : public QObject
{
Q_OBJECT
public:
~TestViewporterInterface() override;
private Q_SLOTS:
void initTestCase();
void testCropScale();
private:
KWayland::Client::ConnectionThread *m_connection;
KWayland::Client::EventQueue *m_queue;
KWayland::Client::Compositor *m_clientCompositor;
KWayland::Client::ShmPool *m_shm;
QThread *m_thread;
Display m_display;
CompositorInterface *m_serverCompositor;
Viewporter *m_viewporter;
};
static const QString s_socketName = QStringLiteral("kwin-wayland-server-viewporter-test-0");
void TestViewporterInterface::initTestCase()
{
m_display.setSocketName(s_socketName);
m_display.start();
QVERIFY(m_display.isRunning());
m_display.createShm();
m_display.createViewporter();
m_serverCompositor = m_display.createCompositor(this);
m_serverCompositor->create();
QVERIFY(m_serverCompositor->isValid());
m_connection = new KWayland::Client::ConnectionThread;
QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
m_connection->setSocketName(s_socketName);
m_thread = new QThread(this);
m_connection->moveToThread(m_thread);
m_thread->start();
m_connection->initConnection();
QVERIFY(connectedSpy.wait());
QVERIFY(!m_connection->connections().isEmpty());
m_queue = new KWayland::Client::EventQueue(this);
QVERIFY(!m_queue->isValid());
m_queue->setup(m_connection);
QVERIFY(m_queue->isValid());
auto registry = new KWayland::Client::Registry(this);
connect(registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, registry](const QByteArray &interface, quint32 id, quint32 version) {
if (interface == QByteArrayLiteral("wp_viewporter")) {
m_viewporter = new Viewporter();
m_viewporter->init(*registry, id, version);
}
});
QSignalSpy allAnnouncedSpy(registry, &KWayland::Client::Registry::interfaceAnnounced);
QSignalSpy compositorSpy(registry, &KWayland::Client::Registry::compositorAnnounced);
QSignalSpy shmSpy(registry, &KWayland::Client::Registry::shmAnnounced);
registry->setEventQueue(m_queue);
registry->create(m_connection->display());
QVERIFY(registry->isValid());
registry->setup();
QVERIFY(allAnnouncedSpy.wait());
m_clientCompositor = registry->createCompositor(compositorSpy.first().first().value<quint32>(),
compositorSpy.first().last().value<quint32>(), this);
QVERIFY(m_clientCompositor->isValid());
m_shm = registry->createShmPool(shmSpy.first().first().value<quint32>(),
shmSpy.first().last().value<quint32>(), this);
QVERIFY(m_shm->isValid());
}
TestViewporterInterface::~TestViewporterInterface()
{
if (m_viewporter) {
delete m_viewporter;
m_viewporter = nullptr;
}
if (m_shm) {
delete m_shm;
m_shm = nullptr;
}
if (m_queue) {
delete m_queue;
m_queue = nullptr;
}
if (m_thread) {
m_thread->quit();
m_thread->wait();
delete m_thread;
m_thread = nullptr;
}
m_connection->deleteLater();
m_connection = nullptr;
}
void TestViewporterInterface::testCropScale()
{
// Create a test surface.
QSignalSpy serverSurfaceCreatedSpy(m_serverCompositor, &CompositorInterface::surfaceCreated);
QVERIFY(serverSurfaceCreatedSpy.isValid());
QScopedPointer<KWayland::Client::Surface> clientSurface(m_clientCompositor->createSurface(this));
QVERIFY(serverSurfaceCreatedSpy.wait());
SurfaceInterface *serverSurface = serverSurfaceCreatedSpy.first().first().value<SurfaceInterface *>();
QVERIFY(serverSurface);
QSignalSpy serverSurfaceMappedSpy(serverSurface, &SurfaceInterface::mapped);
QVERIFY(serverSurfaceMappedSpy.isValid());
QSignalSpy serverSurfaceSizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged);
QVERIFY(serverSurfaceSizeChangedSpy.isValid());
QSignalSpy surfaceToBufferMatrixChangedSpy(serverSurface, &SurfaceInterface::surfaceToBufferMatrixChanged);
QVERIFY(surfaceToBufferMatrixChangedSpy.isValid());
// Map the surface.
QImage image(QSize(200, 100), QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::black);
KWayland::Client::Buffer::Ptr buffer = m_shm->createBuffer(image);
clientSurface->attachBuffer(buffer);
clientSurface->setScale(2);
clientSurface->damage(image.rect());
clientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(serverSurfaceMappedSpy.wait());
QCOMPARE(surfaceToBufferMatrixChangedSpy.count(), 1);
QCOMPARE(serverSurface->size(), QSize(100, 50));
QCOMPARE(serverSurface->mapToBuffer(QPointF(0, 0)), QPointF(0, 0));
// Create a viewport for the surface.
QScopedPointer<Viewport> clientViewport(new Viewport);
clientViewport->init(m_viewporter->get_viewport(*clientSurface));
// Crop the surface.
clientViewport->set_source(wl_fixed_from_double(10), wl_fixed_from_double(10),
wl_fixed_from_double(30), wl_fixed_from_double(20));
clientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(serverSurfaceSizeChangedSpy.wait());
QCOMPARE(surfaceToBufferMatrixChangedSpy.count(), 2);
QCOMPARE(serverSurface->size(), QSize(30, 20));
QCOMPARE(serverSurface->mapToBuffer(QPointF(0, 0)), QPointF(20, 20));
// Scale the surface.
clientViewport->set_destination(500, 250);
clientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(serverSurfaceSizeChangedSpy.wait());
QCOMPARE(surfaceToBufferMatrixChangedSpy.count(), 3);
QCOMPARE(serverSurface->size(), QSize(500, 250));
QCOMPARE(serverSurface->mapToBuffer(QPointF(0, 0)), QPointF(20, 20));
// If the viewport is destroyed, the crop and scale state will be unset on a next commit.
clientViewport->destroy();
clientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(serverSurfaceSizeChangedSpy.wait());
QCOMPARE(surfaceToBufferMatrixChangedSpy.count(), 4);
QCOMPARE(serverSurface->size(), QSize(100, 50));
QCOMPARE(serverSurface->mapToBuffer(QPointF(0, 0)), QPointF(0, 0));
}
QTEST_GUILESS_MAIN(TestViewporterInterface)
#include "test_viewporter_interface.moc"