Implement wp-fractional-scale-v1

This allows clients to provide buffers at native resolutions when
fractional scaling is used.

Virtual backend is adjusted to support scales as floats
master
David Edmundson 2 years ago
parent 361828dfbe
commit c4b134da8d

@ -196,7 +196,7 @@ set_package_properties(Wayland PROPERTIES
PURPOSE "Required for building KWin with Wayland support"
)
find_package(WaylandProtocols 1.30)
find_package(WaylandProtocols 1.31)
set_package_properties(WaylandProtocols PROPERTIES
TYPE REQUIRED
PURPOSE "Collection of Wayland protocols that add functionality not available in the Wayland core protocol"

@ -34,6 +34,10 @@ if (QT_MAJOR_VERSION EQUAL "5")
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml
BASENAME kde-output-management-v2
)
ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework
PROTOCOL ${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
BASENAME fractional-scale-v1
)
else()
qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
NO_INCLUDE_CORE_ONLY
@ -47,6 +51,7 @@ else()
${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
${WaylandProtocols_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml
${WaylandProtocols_DATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml
)
@ -142,6 +147,7 @@ integrationTest(WAYLAND_ONLY NAME testScreens SRCS screens_test.cpp)
integrationTest(WAYLAND_ONLY NAME testScreenEdges SRCS screenedges_test.cpp)
integrationTest(WAYLAND_ONLY NAME testOutputChanges SRCS outputchanges_test.cpp)
integrationTest(WAYLAND_ONLY NAME testTiles SRCS tiles_test.cpp)
integrationTest(WAYLAND_ONLY NAME testFractionalScaling SRCS fractional_scaling_test.cpp)
qt_add_dbus_interfaces(DBUS_SRCS ${CMAKE_BINARY_DIR}/src/org.kde.kwin.VirtualKeyboard.xml)
integrationTest(WAYLAND_ONLY NAME testVirtualKeyboardDBus SRCS test_virtualkeyboard_dbus.cpp ${DBUS_SRCS})

@ -515,16 +515,10 @@ void ActivationTest::stackScreensHorizontally()
QRect(1280, 0, 1280, 1024),
};
const QVector<int> screenScales{
1,
1,
};
QMetaObject::invokeMethod(kwinApp()->outputBackend(),
"setVirtualOutputs",
Qt::DirectConnection,
Q_ARG(QVector<QRect>, screenGeometries),
Q_ARG(QVector<int>, screenScales));
Q_ARG(QVector<QRect>, screenGeometries));
}
void ActivationTest::stackScreensVertically()
@ -537,16 +531,10 @@ void ActivationTest::stackScreensVertically()
QRect(0, 1024, 1280, 1024),
};
const QVector<int> screenScales{
1,
1,
};
QMetaObject::invokeMethod(kwinApp()->outputBackend(),
"setVirtualOutputs",
Qt::DirectConnection,
Q_ARG(QVector<QRect>, screenGeometries),
Q_ARG(QVector<int>, screenScales));
Q_ARG(QVector<QRect>, screenGeometries));
}
}

@ -0,0 +1,103 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kwin_wayland_test.h"
#include "core/output.h"
#include "core/outputbackend.h"
#include "wayland/clientconnection.h"
#include "wayland/display.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/output.h>
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/surface.h>
#include <QDBusConnection>
// system
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <csignal>
using namespace KWin;
static const QString s_socketName = QStringLiteral("wayland_test_kwin_fractionalScale-0");
class TestFractionalScale : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testShow();
};
void TestFractionalScale::initTestCase()
{
qRegisterMetaType<KWin::Window *>();
qRegisterMetaType<KWayland::Client::Output *>();
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
QVERIFY(waylandServer()->init(s_socketName));
QMetaObject::invokeMethod(kwinApp()->outputBackend(),
"setVirtualOutputs",
Qt::DirectConnection,
Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)),
Q_ARG(QVector<qreal>, QVector<qreal>() << 1.25 << 2.0));
kwinApp()->start();
QVERIFY(applicationStartedSpy.wait());
const auto outputs = workspace()->outputs();
QCOMPARE(outputs.count(), 2);
QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1024, 819));
QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 640, 512));
QCOMPARE(outputs[0]->scale(), 1.25);
QCOMPARE(outputs[1]->scale(), 2.0);
}
void TestFractionalScale::init()
{
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::FractionalScaleManagerV1));
workspace()->setActiveOutput(QPoint(640, 512));
// put mouse in the middle of screen one
KWin::Cursors::self()->mouse()->setPos(QPoint(640, 512));
}
void TestFractionalScale::cleanup()
{
Test::destroyWaylandConnection();
}
void TestFractionalScale::testShow()
{
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::FractionalScaleV1> fractionalScale(Test::createFractionalScaleV1(surface.get()));
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
// above call commits the surface and blocks for the configure event. We should have received the scale already
// We are sent the value in 120ths
QCOMPARE(fractionalScale->preferredScale(), 1.25 * 120);
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
QCOMPARE(fractionalScale->preferredScale(), 1.25 * 120);
}
WAYLANDTEST_MAIN(TestFractionalScale)
#include "fractional_scaling_test.moc"

@ -638,7 +638,7 @@ void InternalWindowTest::testScale()
{
QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection,
Q_ARG(QVector<QRect>, QVector<QRect>({QRect(0, 0, 1280, 1024), QRect(1280 / 2, 0, 1280, 1024)})),
Q_ARG(QVector<int>, QVector<int>({2, 2})));
Q_ARG(QVector<qreal>, QVector<qreal>({2, 2})));
QSignalSpy windowAddedSpy(workspace(), &Workspace::internalWindowAdded);
HelperWindow win;

@ -196,4 +196,24 @@ XwaylandInterface *WaylandTestApplication::xwayland() const
{
return m_xwayland.get();
}
Test::FractionalScaleManagerV1::~FractionalScaleManagerV1()
{
destroy();
}
Test::FractionalScaleV1::~FractionalScaleV1()
{
destroy();
}
int Test::FractionalScaleV1::preferredScale()
{
return m_preferredScale;
}
void Test::FractionalScaleV1::wp_fractional_scale_v1_preferred_scale(uint32_t scale)
{
m_preferredScale = scale;
}
}

@ -18,6 +18,7 @@
#include <KWayland/Client/surface.h>
#include "qwayland-fractional-scale-v1.h"
#include "qwayland-idle-inhibit-unstable-v1.h"
#include "qwayland-input-method-unstable-v1.h"
#include "qwayland-kde-output-device-v2.h"
@ -460,6 +461,27 @@ private:
struct ::zwp_input_method_context_v1 *m_context = nullptr;
};
class FractionalScaleManagerV1 : public QObject, public QtWayland::wp_fractional_scale_manager_v1
{
Q_OBJECT
public:
~FractionalScaleManagerV1() override;
};
class FractionalScaleV1 : public QObject, public QtWayland::wp_fractional_scale_v1
{
Q_OBJECT
public:
~FractionalScaleV1() override;
int preferredScale();
protected:
void wp_fractional_scale_v1_preferred_scale(uint32_t scale) override;
private:
int m_preferredScale = 120;
};
enum class AdditionalWaylandInterface {
Seat = 1 << 0,
Decoration = 1 << 1,
@ -476,6 +498,7 @@ enum class AdditionalWaylandInterface {
LayerShellV1 = 1 << 12,
TextInputManagerV3 = 1 << 13,
OutputDeviceV2 = 1 << 14,
FractionalScaleManagerV1 = 1 << 15,
};
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
@ -595,6 +618,8 @@ enum class CreationSetup {
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface,
KWayland::Client::Output *output);
FractionalScaleV1 *createFractionalScaleV1(KWayland::Client::Surface *surface);
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr);
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface,
CreationSetup configureMode,

@ -251,6 +251,7 @@ static struct
QtWayland::zwp_input_method_context_v1 *inputMethodContextV1 = nullptr;
LayerShellV1 *layerShellV1 = nullptr;
TextInputManagerV3 *textInputManagerV3 = nullptr;
FractionalScaleManagerV1 *fractionalScaleManagerV1 = nullptr;
} s_waylandConnection;
MockInputMethod *inputMethod()
@ -415,6 +416,13 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
return;
}
}
if (flags & AdditionalWaylandInterface::FractionalScaleManagerV1) {
if (interface == wp_fractional_scale_manager_v1_interface.name) {
s_waylandConnection.fractionalScaleManagerV1 = new FractionalScaleManagerV1();
s_waylandConnection.fractionalScaleManagerV1->init(*registry, name, version);
return;
}
}
});
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
@ -539,6 +547,8 @@ void destroyWaylandConnection()
s_waylandConnection.layerShellV1 = nullptr;
delete s_waylandConnection.outputManagementV2;
s_waylandConnection.outputManagementV2 = nullptr;
delete s_waylandConnection.fractionalScaleManagerV1;
s_waylandConnection.fractionalScaleManagerV1 = nullptr;
delete s_waylandConnection.queue; // Must be destroyed last
s_waylandConnection.queue = nullptr;
@ -796,6 +806,18 @@ QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Clien
return s;
}
FractionalScaleV1 *createFractionalScaleV1(KWayland::Client::Surface *surface)
{
if (!s_waylandConnection.fractionalScaleManagerV1) {
qWarning() << "Unable to create fractional scale surface. The global is not bound";
return nullptr;
}
auto scale = new FractionalScaleV1();
scale->init(s_waylandConnection.fractionalScaleManagerV1->get_fractional_scale(*surface));
return scale;
}
static void waitForConfigured(XdgSurface *shellSurface)
{
QSignalSpy surfaceConfigureRequestedSpy(shellSurface, &XdgSurface::configureRequested);

@ -83,7 +83,7 @@ Output *VirtualBackend::addOutput(const QSize &size, qreal scale)
return output;
}
void VirtualBackend::setVirtualOutputs(const QVector<QRect> &geometries, QVector<int> scales)
void VirtualBackend::setVirtualOutputs(const QVector<QRect> &geometries, QVector<qreal> scales)
{
Q_ASSERT(scales.size() == 0 || scales.size() == geometries.size());

@ -42,7 +42,8 @@ public:
std::unique_ptr<OpenGLBackend> createOpenGLBackend() override;
Output *addOutput(const QSize &size, qreal scale);
Q_INVOKABLE void setVirtualOutputs(const QVector<QRect> &geometries, QVector<int> scales = QVector<int>());
Q_INVOKABLE void setVirtualOutputs(const QVector<QRect> &geometries, QVector<qreal> scales = QVector<qreal>());
Outputs outputs() const override;

@ -201,6 +201,11 @@ ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
BASENAME xwayland-shell-v1
)
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
PROTOCOL ${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
BASENAME fractional-scale-v1
)
target_sources(kwin PRIVATE
abstract_data_source.cpp
abstract_drop_handler.cpp
@ -225,6 +230,7 @@ target_sources(kwin PRIVATE
drmclientbuffer.cpp
drmlease_v1_interface.cpp
fakeinput_interface.cpp
fractionalscale_v1_interface.cpp
filtered_display.cpp
idle_interface.cpp
idleinhibit_v1_interface.cpp

@ -0,0 +1,92 @@
/*
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "fractionalscale_v1_interface.h"
#include "display.h"
#include "fractionalscale_v1_interface_p.h"
#include "surface_interface_p.h"
static const int s_version = 1;
namespace KWaylandServer
{
class FractionalScaleManagerV1InterfacePrivate : public QtWaylandServer::wp_fractional_scale_manager_v1
{
protected:
void wp_fractional_scale_manager_v1_destroy(Resource *resource) override;
void wp_fractional_scale_manager_v1_get_fractional_scale(Resource *resource, uint32_t id, wl_resource *surface) override;
};
void FractionalScaleManagerV1InterfacePrivate::wp_fractional_scale_manager_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void FractionalScaleManagerV1InterfacePrivate::wp_fractional_scale_manager_v1_get_fractional_scale(Resource *resource, uint32_t id, struct ::wl_resource *surface_resource)
{
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
FractionalScaleV1Interface *scaleIface = FractionalScaleV1Interface::get(surface);
if (scaleIface) {
wl_resource_post_error(resource->handle, error_fractional_scale_exists, "the specified surface already has a fractional scale");
return;
}
wl_resource *surfaceScalerResource = wl_resource_create(resource->client(), &wp_fractional_scale_v1_interface, resource->version(), id);
new FractionalScaleV1Interface(surface, surfaceScalerResource);
}
FractionalScaleV1Interface::FractionalScaleV1Interface(SurfaceInterface *surface, wl_resource *resource)
: QtWaylandServer::wp_fractional_scale_v1(resource)
, surface(surface)
{
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->fractionalScaleExtension = this;
setPreferredScale(surfacePrivate->preferredScale);
}
FractionalScaleV1Interface::~FractionalScaleV1Interface()
{
if (surface) {
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
surfacePrivate->fractionalScaleExtension = nullptr;
}
}
FractionalScaleV1Interface *FractionalScaleV1Interface::get(SurfaceInterface *surface)
{
return SurfaceInterfacePrivate::get(surface)->fractionalScaleExtension;
}
void FractionalScaleV1Interface::setPreferredScale(qreal scale)
{
send_preferred_scale(std::round(scale * 120));
}
void FractionalScaleV1Interface::wp_fractional_scale_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void FractionalScaleV1Interface::wp_fractional_scale_v1_destroy_resource(Resource *)
{
delete this;
}
FractionalScaleManagerV1Interface::FractionalScaleManagerV1Interface(Display *display, QObject *parent)
: QObject(parent)
, d(new FractionalScaleManagerV1InterfacePrivate)
{
d->init(*display, s_version);
}
FractionalScaleManagerV1Interface::~FractionalScaleManagerV1Interface()
{
}
} // namespace KWaylandServer

@ -0,0 +1,31 @@
/*
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "kwin_export.h"
#include <QObject>
#include <memory>
namespace KWaylandServer
{
class Display;
class FractionalScaleManagerV1InterfacePrivate;
class KWIN_EXPORT FractionalScaleManagerV1Interface : public QObject
{
Q_OBJECT
public:
explicit FractionalScaleManagerV1Interface(Display *display, QObject *parent = nullptr);
~FractionalScaleManagerV1Interface() override;
private:
std::unique_ptr<FractionalScaleManagerV1InterfacePrivate> d;
};
} // namespace KWaylandServer

@ -0,0 +1,33 @@
/*
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "qwayland-server-fractional-scale-v1.h"
#include <QPointer>
namespace KWaylandServer
{
class SurfaceInterface;
class FractionalScaleV1Interface : protected QtWaylandServer::wp_fractional_scale_v1
{
public:
FractionalScaleV1Interface(SurfaceInterface *surface, wl_resource *resource);
~FractionalScaleV1Interface() override;
static FractionalScaleV1Interface *get(SurfaceInterface *surface);
void setPreferredScale(qreal scale);
QPointer<SurfaceInterface> surface;
protected:
void wp_fractional_scale_v1_destroy(Resource *resource) override;
void wp_fractional_scale_v1_destroy_resource(Resource *resource) override;
};
} // namespace KWaylandServer

@ -10,6 +10,7 @@
#include "compositor_interface.h"
#include "contenttype_v1_interface.h"
#include "display.h"
#include "fractionalscale_v1_interface_p.h"
#include "idleinhibit_v1_interface_p.h"
#include "linuxdmabufv1clientbuffer.h"
#include "pointerconstraints_v1_interface_p.h"
@ -72,6 +73,8 @@ void SurfaceInterfacePrivate::addChild(SubSurfaceInterface *child)
cached.above.append(child);
current.above.append(child);
child->surface()->setOutputs(outputs);
child->surface()->setPreferredScale(preferredScale);
Q_EMIT q->childSubSurfaceAdded(child);
Q_EMIT q->childSubSurfacesChanged();
}
@ -1106,4 +1109,22 @@ PresentationHint SurfaceInterface::presentationHint() const
return d->current.presentationHint;
}
void SurfaceInterface::setPreferredScale(qreal scale)
{
if (scale == d->preferredScale) {
return;
}
d->preferredScale = scale;
if (d->fractionalScaleExtension) {
d->fractionalScaleExtension->setPreferredScale(scale);
}
for (auto child : qAsConst(d->current.below)) {
child->surface()->setPreferredScale(scale);
}
for (auto child : qAsConst(d->current.above)) {
child->surface()->setPreferredScale(scale);
}
}
} // namespace KWaylandServer

@ -346,6 +346,12 @@ public:
*/
PresentationHint presentationHint() const;
/**
* Sets a preferred scale that clients should provide buffers in
* @param scale
*/
void setPreferredScale(qreal scale);
Q_SIGNALS:
/**
* This signal is emitted when the underlying wl_surface resource is about to be freed.

@ -21,6 +21,7 @@ class SurfaceRole;
class ViewportInterface;
class ContentTypeV1Interface;
class TearingControlV1Interface;
class FractionalScaleV1Interface;
struct SurfaceState
{
@ -131,6 +132,7 @@ public:
qreal pendingScaleOverride = 1.;
QVector<OutputInterface *> outputs;
qreal preferredScale = 1.0;
LockedPointerV1Interface *lockedPointer = nullptr;
ConfinedPointerV1Interface *confinedPointer = nullptr;
@ -141,6 +143,7 @@ public:
ViewportInterface *viewportExtension = nullptr;
std::unique_ptr<LinuxDmaBufV1Feedback> dmabufFeedbackV1;
QPointer<ContentTypeV1Interface> contentTypeInterface;
FractionalScaleV1Interface *fractionalScaleExtension = nullptr;
ClientConnection *client = nullptr;
TearingControlV1Interface *tearing = nullptr;

@ -34,6 +34,7 @@
#include "wayland/dpms_interface.h"
#include "wayland/drmlease_v1_interface.h"
#include "wayland/filtered_display.h"
#include "wayland/fractionalscale_v1_interface.h"
#include "wayland/idle_interface.h"
#include "wayland/idleinhibit_v1_interface.h"
#include "wayland/idlenotify_v1_interface.h"
@ -408,6 +409,7 @@ bool WaylandServer::init(InitializationFlags flags)
});
new ViewporterInterface(m_display, m_display);
new FractionalScaleManagerV1Interface(m_display, m_display);
m_display->createShm();
m_seat = new SeatInterface(m_display, m_display);
new PointerGesturesV1Interface(m_display, m_display);

@ -159,6 +159,9 @@ bool WaylandWindow::belongsToDesktop() const
void WaylandWindow::updateClientOutputs()
{
surface()->setOutputs(waylandServer()->display()->outputsIntersecting(frameGeometry().toAlignedRect()));
if (output()) {
surface()->setPreferredScale(output()->scale());
}
}
void WaylandWindow::updateIcon()

Loading…
Cancel
Save