Add support for the wp_viewporter protocol

The wp_viewporter compositor extension allows clients to crop and scale
their surface. It can be useful for applications such as video players
because it may potentially reduce their power usage.
This commit is contained in:
Vlad Zahorodnii 2020-05-24 13:46:27 +03:00
parent 99ef521e6d
commit 963551d775
11 changed files with 587 additions and 29 deletions

View file

@ -58,6 +58,7 @@ set(SERVER_LIB_SRCS
textinput_interface_v0.cpp
textinput_interface_v2.cpp
touch_interface.cpp
viewporter_interface.cpp
xdgdecoration_v1_interface.cpp
xdgforeign_interface.cpp
xdgforeign_v2_interface.cpp
@ -238,6 +239,11 @@ ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
BASENAME keyboard-shortcuts-inhibit-unstable-v1
)
ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml
BASENAME viewporter
)
set(SERVER_GENERATED_SRCS
${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h
@ -386,6 +392,7 @@ set(SERVER_LIB_HEADERS
tablet_interface.h
textinput_interface.h
touch_interface.h
viewporter_interface.h
xdgdecoration_v1_interface.h
xdgforeign_interface.h
xdgoutput_interface.h

View file

@ -64,3 +64,14 @@ target_link_libraries(testKeyboardShortcutsInhibitorInterface Qt5::Test Plasma::
add_test(NAME kwayland-testKeyboardShortcutsInhibitorInterface COMMAND testKeyboardShortcutsInhibitorInterface)
ecm_mark_as_test(testKeyboardShortcutsInhibitorInterface)
########################################################
# Test Viewporter Interface
########################################################
ecm_add_qtwayland_client_protocol(VIEWPORTER_SRCS
PROTOCOL ${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml
BASENAME viewporter
)
add_executable(testViewporterInterface test_viewporter_interface.cpp ${VIEWPORTER_SRCS})
target_link_libraries(testViewporterInterface Qt5::Test Plasma::KWaylandServer KF5::WaylandClient Wayland::Client)
add_test(NAME kwayland-testViewporterInterface COMMAND testViewporterInterface)
ecm_mark_as_test(testViewporterInterface)

View file

@ -0,0 +1,193 @@
/*
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());
// Map the surface.
QImage image(QSize(100, 50), QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::black);
KWayland::Client::Buffer::Ptr buffer = m_shm->createBuffer(image);
clientSurface->attachBuffer(buffer);
clientSurface->damage(image.rect());
clientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(serverSurfaceMappedSpy.wait());
QCOMPARE(serverSurface->size(), QSize(100, 50));
QCOMPARE(serverSurface->viewport(), QRectF(0, 0, 100, 50));
// 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(serverSurface->size(), QSize(30, 20));
QCOMPARE(serverSurface->viewport(), QRectF(10, 10, 30, 20));
// Scale the surface.
clientViewport->set_destination(500, 250);
clientSurface->commit(KWayland::Client::Surface::CommitFlag::None);
QVERIFY(serverSurfaceSizeChangedSpy.wait());
QCOMPARE(serverSurface->size(), QSize(500, 250));
QCOMPARE(serverSurface->viewport(), QRectF(10, 10, 30, 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(serverSurface->size(), QSize(100, 50));
QCOMPARE(serverSurface->viewport(), QRectF(0, 0, 100, 50));
}
QTEST_GUILESS_MAIN(TestViewporterInterface)
#include "test_viewporter_interface.moc"

View file

@ -39,6 +39,7 @@
#include "subcompositor_interface.h"
#include "tablet_interface.h"
#include "textinput_interface_p.h"
#include "viewporter_interface.h"
#include "xdgdecoration_v1_interface.h"
#include "xdgforeign_interface.h"
#include "xdgoutput_interface.h"
@ -516,6 +517,13 @@ KeyboardShortcutsInhibitManagerV1Interface *Display::createKeyboardShortcutsInhi
return d;
}
ViewporterInterface *Display::createViewporter(QObject *parent)
{
auto viewporter = new ViewporterInterface(this, parent);
connect(this, &Display::aboutToTerminate, viewporter, [viewporter] { delete viewporter; });
return viewporter;
}
void Display::createShm()
{
Q_ASSERT(d->display);

View file

@ -79,6 +79,7 @@ class LinuxDmabufUnstableV1Interface;
class TabletManagerInterface;
class DataControlDeviceManagerV1Interface;
class KeyboardShortcutsInhibitManagerV1Interface;
class ViewporterInterface;
/**
* @brief Class holding the Wayland server display loop.
@ -327,6 +328,11 @@ public:
*/
KeyboardShortcutsInhibitManagerV1Interface *createKeyboardShortcutsInhibitManagerV1(QObject *parent = nullptr);
/**
* Creates the viewporter compositor extension.
*/
ViewporterInterface *createViewporter(QObject *parent = nullptr);
/**
* Gets the ClientConnection for the given @p client.
* If there is no ClientConnection yet for the given @p client, it will be created.

View file

@ -329,16 +329,13 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em
const bool slideChanged = source->slideIsSet;
const bool childrenChanged = source->childrenChanged;
const bool visibilityChanged = bufferChanged && (bool(source->buffer) != bool(target->buffer));
bool sizeChanged = false;
auto buffer = target->buffer;
const QRectF oldViewport = target->viewport;
const QSize oldSize = target->size;
if (bufferChanged) {
// TODO: is the reffing correct for subsurfaces?
QSize oldSize;
if (target->buffer) {
oldSize = target->buffer->size();
if (emitChanged) {
target->buffer->unref();
QObject::disconnect(target->buffer, &BufferInterface::sizeChanged, q, &SurfaceInterface::sizeChanged);
} else {
delete target->buffer;
target->buffer = nullptr;
@ -347,21 +344,22 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em
if (source->buffer) {
if (emitChanged) {
source->buffer->ref();
QObject::connect(source->buffer, &BufferInterface::sizeChanged, q, &SurfaceInterface::sizeChanged);
}
const QSize newSize = source->buffer->size();
sizeChanged = newSize.isValid() && newSize != oldSize;
}
buffer = source->buffer;
}
// copy values
if (bufferChanged) {
target->buffer = buffer;
target->buffer = source->buffer;
target->offset = source->offset;
target->damage = source->damage;
target->bufferDamage = source->bufferDamage;
target->bufferIsSet = source->bufferIsSet;
}
if (source->sourceGeometryIsSet) {
target->sourceGeometry = source->sourceGeometry;
target->sourceGeometryIsSet = true;
}
if (source->destinationSizeIsSet) {
target->destinationSize = source->destinationSize;
target->destinationSizeIsSet = true;
}
if (childrenChanged) {
target->childrenChanged = source->childrenChanged;
target->children = source->children;
@ -414,6 +412,21 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em
if (!emitChanged) {
return;
}
if (target->buffer) {
if (target->sourceGeometry.isValid()) {
target->viewport = target->sourceGeometry;
} else {
target->viewport = QRectF(QPointF(0, 0), invertBufferTransform(target, target->buffer->size()));
}
if (target->destinationSize.isValid()) {
target->size = target->destinationSize;
} else {
target->size = target->viewport.size().toSize();
}
} else {
target->viewport = QRectF();
target->size = QSize();
}
if (opaqueRegionChanged) {
emit q->opaqueChanged(target->opaque);
}
@ -422,9 +435,6 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em
}
if (scaleFactorChanged) {
emit q->scaleChanged(target->scale);
if (buffer && !sizeChanged) {
emit q->sizeChanged();
}
}
if (transformChanged) {
emit q->transformChanged(target->transform);
@ -457,9 +467,12 @@ void SurfaceInterface::Private::swapStates(State *source, State *target, bool em
}
}
}
if (sizeChanged) {
if (target->size != oldSize) {
emit q->sizeChanged();
}
if (target->viewport != oldViewport) {
emit q->viewportChanged();
}
if (shadowChanged) {
emit q->shadowChanged();
}
@ -742,11 +755,13 @@ QPointer< SubSurfaceInterface > SurfaceInterface::subSurface() const
QSize SurfaceInterface::size() const
{
Q_D();
// TODO: apply transform to the buffer size
if (d->current.buffer) {
return d->current.buffer->size() / scale();
return d->current.size;
}
return QSize();
QRectF SurfaceInterface::viewport() const
{
Q_D();
return d->current.viewport;
}
QRect SurfaceInterface::boundingRect() const
@ -1084,10 +1099,55 @@ QSizeF SurfaceInterface::Private::invertBufferTransform(const State *state, cons
return transformed;
}
QPointF SurfaceInterface::Private::viewportTransform(const State *state, const QPointF &point) const
{
if (!viewportExtension)
return point;
qreal x = state->size.width() * (point.x() - state->viewport.x()) / state->viewport.width();
qreal y = state->size.height() * (point.y() - state->viewport.y()) / state->viewport.height();
return QPointF(x, y);
}
QPointF SurfaceInterface::Private::invertViewportTransform(const State *state, const QPointF &point) const
{
if (!viewportExtension)
return point;
qreal x = point.x() * state->viewport.width() / state->size.width() + state->viewport.x();
qreal y = point.y() * state->viewport.height() / state->size.height() + state->viewport.y();
return QPointF(x, y);
}
QSizeF SurfaceInterface::Private::viewportTransform(const State *state, const QSizeF &size) const
{
if (!viewportExtension)
return size;
qreal width = size.width() * state->size.width() / state->viewport.width();
qreal height = size.height() * state->size.height() / state->viewport.height();
return QSizeF(width, height);
}
QSizeF SurfaceInterface::Private::invertViewportTransform(const State *state, const QSizeF &size) const
{
if (!viewportExtension)
return size;
qreal width = size.width() * state->viewport.width() / state->size.width();
qreal height = size.height() * state->viewport.height() / state->size.height();
return QSize(width, height);
}
QPointF SurfaceInterface::Private::mapToBuffer(const State *state, const QPointF &point) const
{
QPointF transformed = point;
transformed = invertViewportTransform(state, transformed);
transformed = bufferTransform(state, transformed);
return transformed;
@ -1098,6 +1158,7 @@ QPointF SurfaceInterface::Private::mapFromBuffer(const State *state, const QPoin
QPointF transformed = point;
transformed = invertBufferTransform(state, transformed);
transformed = viewportTransform(state, transformed);
return transformed;
}
@ -1106,6 +1167,7 @@ QSizeF SurfaceInterface::Private::mapToBuffer(const State *state, const QSizeF &
{
QSizeF transformed = size;
transformed = invertViewportTransform(state, transformed);
transformed = bufferTransform(state, transformed);
return transformed;
@ -1116,6 +1178,7 @@ QSizeF SurfaceInterface::Private::mapFromBuffer(const State *state, const QSizeF
QSizeF transformed = size;
transformed = invertBufferTransform(state, transformed);
transformed = viewportTransform(state, transformed);
return transformed;
}

View file

@ -185,11 +185,17 @@ public:
BufferInterface *buffer();
QPoint offset() const;
/**
* The size of the Surface in global compositor space.
* @see For buffer size use BufferInterface::size
* from SurfaceInterface::buffer
* @since 5.3
**/
* Returns the rectangle that indicates what area of the attached buffer is displayed.
*
* The size of the viewport rectangle doesn't correspond to the size of the surface.
*/
QRectF viewport() const;
/**
* Returns the current size of the surface, in surface coordinates.
*
* Note that there is no direct relationship between the surface size and the buffer size.
* In order to determine the size of the currently attached buffer, use buffer()->size().
*/
QSize size() const;
/**
* Returns the rectangle that bounds this surface and all of its sub-surfaces.
@ -378,7 +384,19 @@ Q_SIGNALS:
void damaged(const QRegion&);
void opaqueChanged(const QRegion&);
void inputChanged(const QRegion&);
/**
* This signal is emitted when the scale of the attached buffer has changed.
*
* Note that the compositor has to re-compute the texture coordinates after the scale
* of the buffer has been changed.
*/
void scaleChanged(qint32);
/**
* This signal is emitted when the buffer transform has changed.
*
* Note that the compositor has to re-compute the texture coordinates after the buffer
* transform has been changed.
*/
void transformChanged(KWaylandServer::OutputInterface::Transform);
/**
* Emitted when the Surface becomes visible, i.e. a non-null buffer has been attached.
@ -389,9 +407,21 @@ Q_SIGNALS:
**/
void unmapped();
/**
* This signal is emitted when the surface size has changed.
*
* Note that the compositor has to re-compute the texture coordinates after the surface
* size has been changed.
*
* @since 5.3
**/
*/
void sizeChanged();
/**
* This signal is emitted when the viewport rectangle has changed.
*
* Note that the compositor has to re-compute the texture coordinates after the viewport
* rectangle has been changed.
*/
void viewportChanged();
/**
* @since 5.4
**/
@ -462,6 +492,7 @@ private:
friend class IdleInhibitManagerUnstableV1Interface;
friend class PointerConstraintsUnstableV1Interface;
friend class SurfaceRole;
friend class ViewportInterface;
explicit SurfaceInterface(CompositorInterface *parent, wl_resource *parentResource);
class Private;

View file

@ -19,6 +19,7 @@ namespace KWaylandServer
class IdleInhibitorInterface;
class SurfaceRole;
class ViewportInterface;
class SurfaceInterface::Private : public Resource::Private
{
@ -28,6 +29,12 @@ public:
QRegion bufferDamage = QRegion();
QRegion opaque = QRegion();
QRegion input = QRegion();
QRectF sourceGeometry = QRectF();
QSize destinationSize = QSize();
QRectF viewport = QRectF();
QSize size = QSize();
bool sourceGeometryIsSet = false;
bool destinationSizeIsSet = false;
bool inputIsSet = false;
bool opaqueIsSet = false;
bool bufferIsSet = false;
@ -84,6 +91,10 @@ public:
QPointF invertBufferTransform(const State *state, const QPointF &point) const;
QSizeF bufferTransform(const State *state, const QSizeF &size) const;
QSizeF invertBufferTransform(const State *state, const QSizeF &size) const;
QPointF viewportTransform(const State *state, const QPointF &point) const;
QPointF invertViewportTransform(const State *state, const QPointF &point) const;
QSizeF viewportTransform(const State *state, const QSizeF &size) const;
QSizeF invertViewportTransform(const State *state, const QSizeF &size) const;
SurfaceRole *role = nullptr;
@ -105,7 +116,7 @@ public:
QPointer<ConfinedPointerInterface> confinedPointer;
QHash<OutputInterface*, QMetaObject::Connection> outputDestroyedConnections;
QVector<IdleInhibitorInterface*> idleInhibitors;
ViewportInterface *viewportExtension = nullptr;
SurfaceInterface *dataProxy = nullptr;
private:

View file

@ -0,0 +1,152 @@
/*
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 "viewporter_interface.h"
#include "display.h"
#include "surface_interface_p.h"
#include "viewporter_interface_p.h"
static const int s_version = 1;
namespace KWaylandServer
{
class ViewporterInterfacePrivate : public QtWaylandServer::wp_viewporter
{
protected:
void wp_viewporter_destroy(Resource *resource) override;
void wp_viewporter_get_viewport(Resource *resource, uint32_t id, struct ::wl_resource *surface) override;
};
void ViewporterInterfacePrivate::wp_viewporter_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void ViewporterInterfacePrivate::wp_viewporter_get_viewport(Resource *resource, uint32_t id, struct ::wl_resource *surface_resource)
{
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
ViewportInterface *viewport = ViewportInterface::get(surface);
if (viewport) {
wl_resource_post_error(resource->handle, error_viewport_exists,
"the specified surface already has a viewport");
return;
}
wl_resource *viewportResource = wl_resource_create(resource->client(), &wp_viewport_interface,
resource->version(), id);
new ViewportInterface(surface, viewportResource);
}
ViewportInterface::ViewportInterface(SurfaceInterface *surface, wl_resource *resource)
: QtWaylandServer::wp_viewport(resource)
, surface(surface)
{
SurfaceInterface::Private *surfacePrivate = surface->d_func();
surfacePrivate->viewportExtension = this;
}
ViewportInterface::~ViewportInterface()
{
if (surface) {
SurfaceInterface::Private *surfacePrivate = surface->d_func();
surfacePrivate->viewportExtension = nullptr;
}
}
ViewportInterface *ViewportInterface::get(SurfaceInterface *surface)
{
return surface->d_func()->viewportExtension;
}
void ViewportInterface::wp_viewport_destroy_resource(Resource *resource)
{
Q_UNUSED(resource)
delete this;
}
void ViewportInterface::wp_viewport_destroy(Resource *resource)
{
if (surface) {
SurfaceInterface::Private *surfacePrivate = surface->d_func();
surfacePrivate->pending.sourceGeometry = QRectF();
surfacePrivate->pending.sourceGeometryIsSet = true;
surfacePrivate->pending.destinationSize = QSize();
surfacePrivate->pending.destinationSizeIsSet = true;
}
wl_resource_destroy(resource->handle);
}
void ViewportInterface::wp_viewport_set_source(Resource *resource, wl_fixed_t x_fixed, wl_fixed_t y_fixed, wl_fixed_t width_fixed, wl_fixed_t height_fixed)
{
if (!surface) {
wl_resource_post_error(resource->handle, error_no_surface,
"the wl_surface for this viewport no longer exists");
return;
}
const qreal x = wl_fixed_to_double(x_fixed);
const qreal y = wl_fixed_to_double(y_fixed);
const qreal width = wl_fixed_to_double(width_fixed);
const qreal height = wl_fixed_to_double(height_fixed);
if (x == -1 && y == -1 && width == -1 && height == -1) {
SurfaceInterface::Private *surfacePrivate = surface->d_func();
surfacePrivate->pending.sourceGeometry = QRectF();
surfacePrivate->pending.sourceGeometryIsSet = true;
return;
}
if (x < 0 || y < 0 || width <= 0 || height <= 0) {
wl_resource_post_error(resource->handle, error_bad_value, "invalid source geometry");
return;
}
SurfaceInterface::Private *surfacePrivate = surface->d_func();
surfacePrivate->pending.sourceGeometry = QRectF(x, y, width, height);
surfacePrivate->pending.sourceGeometryIsSet = true;
}
void ViewportInterface::wp_viewport_set_destination(Resource *resource, int32_t width, int32_t height)
{
if (!surface) {
wl_resource_post_error(resource->handle, error_no_surface,
"the wl_surface for this viewport no longer exists");
return;
}
if (width == -1 && height == -1) {
SurfaceInterface::Private *surfacePrivate = surface->d_func();
surfacePrivate->pending.destinationSize = QSize();
surfacePrivate->pending.destinationSizeIsSet = true;
return;
}
if (width <= 0 || height <= 0) {
wl_resource_post_error(resource->handle, error_bad_value, "invalid destination size");
return;
}
SurfaceInterface::Private *surfacePrivate = surface->d_func();
surfacePrivate->pending.destinationSize = QSize(width, height);
surfacePrivate->pending.destinationSizeIsSet = true;
}
ViewporterInterface::ViewporterInterface(Display *display, QObject *parent)
: QObject(parent)
, d(new ViewporterInterfacePrivate)
{
d->init(*display, s_version);
}
ViewporterInterface::~ViewporterInterface()
{
}
} // namespace KWaylandServer

View file

@ -0,0 +1,41 @@
/*
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
*/
#pragma once
#include <KWaylandServer/kwaylandserver_export.h>
#include <QObject>
namespace KWaylandServer
{
class Display;
class ViewporterInterfacePrivate;
/**
* The ViewporterInterface is an extension that allows clients to crop and scale surfaces.
*
* The ViewporterInterface extensions provides a way for Wayland clients to crop and scale their
* surfaces. This effectively breaks the direct connection between the buffer and the surface size.
*
* ViewporterInterface corresponds to the Wayland interface @c wp_viewporter.
*
* @since 5.20
*/
class KWAYLANDSERVER_EXPORT ViewporterInterface : public QObject
{
Q_OBJECT
public:
explicit ViewporterInterface(Display *display, QObject *parent = nullptr);
~ViewporterInterface() override;
private:
QScopedPointer<ViewporterInterfacePrivate> d;
};
} // namespace KWaylandServer

View file

@ -0,0 +1,35 @@
/*
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
*/
#pragma once
#include "qwayland-server-viewporter.h"
#include <QPointer>
namespace KWaylandServer
{
class SurfaceInterface;
class ViewportInterface : public QtWaylandServer::wp_viewport
{
public:
ViewportInterface(SurfaceInterface *surface, wl_resource *resource);
~ViewportInterface() override;
static ViewportInterface *get(SurfaceInterface *surface);
QPointer<SurfaceInterface> surface;
protected:
void wp_viewport_destroy_resource(Resource *resource) override;
void wp_viewport_destroy(Resource *resource) override;
void wp_viewport_set_source(Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override;
void wp_viewport_set_destination(Resource *resource, int32_t width, int32_t height) override;
};
} // namespace KWaylandServer