autotests/integration: add a color management test

Only checks for protocol things right now, but will be extended to do more later

(cherry picked from commit e870986406)
wilder/Plasma/6.2
Xaver Hugl 1 year ago
parent 90227d8f76
commit ed3942b070
  1. 12
      autotests/integration/CMakeLists.txt
  2. 12
      autotests/integration/kwin_wayland_test.h
  3. 229
      autotests/integration/test_colormanagement.cpp
  4. 22
      autotests/integration/test_helpers.cpp

@ -14,16 +14,17 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
${private_code_option} ${private_code_option}
FILES FILES
${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v3.xml
${CMAKE_SOURCE_DIR}/src/wayland/protocols/wlr-layer-shell-unstable-v1.xml ${CMAKE_SOURCE_DIR}/src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
${CMAKE_SOURCE_DIR}/src/wayland/protocols/xx-color-management-v4.xml
${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml ${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}/unstable/tablet/tablet-unstable-v2.xml
${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
${WaylandProtocols_DATADIR}/staging/cursor-shape/cursor-shape-v1.xml ${WaylandProtocols_DATADIR}/staging/cursor-shape/cursor-shape-v1.xml
${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml ${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml
${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml ${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
${WaylandProtocols_DATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
${WaylandProtocols_DATADIR}/unstable/tablet/tablet-unstable-v2.xml
${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v3.xml
${WaylandProtocols_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml
@ -141,6 +142,7 @@ integrationTest(NAME testFakeInput SRCS fakeinput_test.cpp)
integrationTest(NAME testSecurityContext SRCS security_context_test.cpp) integrationTest(NAME testSecurityContext SRCS security_context_test.cpp)
integrationTest(NAME testStickyKeys SRCS sticky_keys_test.cpp) integrationTest(NAME testStickyKeys SRCS sticky_keys_test.cpp)
integrationTest(NAME testWorkspace SRCS workspace_test.cpp) integrationTest(NAME testWorkspace SRCS workspace_test.cpp)
integrationTest(NAME testColorManagement SRCS test_colormanagement.cpp)
if(KWIN_BUILD_X11) if(KWIN_BUILD_X11)
integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp LIBS KDecoration2::KDecoration) integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp LIBS KDecoration2::KDecoration)

@ -32,9 +32,10 @@
#include "qwayland-text-input-unstable-v3.h" #include "qwayland-text-input-unstable-v3.h"
#include "qwayland-wlr-layer-shell-unstable-v1.h" #include "qwayland-wlr-layer-shell-unstable-v1.h"
#include "qwayland-xdg-decoration-unstable-v1.h" #include "qwayland-xdg-decoration-unstable-v1.h"
#include "qwayland-xdg-dialog-v1.h"
#include "qwayland-xdg-shell.h" #include "qwayland-xdg-shell.h"
#include "qwayland-xx-color-management-v4.h"
#include "qwayland-zkde-screencast-unstable-v1.h" #include "qwayland-zkde-screencast-unstable-v1.h"
#include "qwayland-xdg-dialog-v1.h"
namespace KWayland namespace KWayland
{ {
@ -600,6 +601,7 @@ enum class AdditionalWaylandInterface {
FakeInput = 1 << 19, FakeInput = 1 << 19,
SecurityContextManagerV1 = 1 << 20, SecurityContextManagerV1 = 1 << 20,
XdgDialogV1 = 1 << 21, XdgDialogV1 = 1 << 21,
ColorManagement = 1 << 22,
}; };
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface) Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
@ -646,6 +648,13 @@ private:
bool m_tabletTool = false; bool m_tabletTool = false;
}; };
class XXColorManagerV4 : public QtWayland::xx_color_manager_v4
{
public:
explicit XXColorManagerV4(::wl_registry *registry, uint32_t id, int version);
~XXColorManagerV4() override;
};
void keyboardKeyPressed(quint32 key, quint32 time); void keyboardKeyPressed(quint32 key, quint32 time);
void keyboardKeyReleased(quint32 key, quint32 time); void keyboardKeyReleased(quint32 key, quint32 time);
void pointerAxisHorizontal(qreal delta, void pointerAxisHorizontal(qreal delta,
@ -706,6 +715,7 @@ ScreencastingV1 *screencasting();
QList<WaylandOutputDeviceV2 *> waylandOutputDevicesV2(); QList<WaylandOutputDeviceV2 *> waylandOutputDevicesV2();
FakeInput *waylandFakeInput(); FakeInput *waylandFakeInput();
SecurityContextManagerV1 *waylandSecurityContextManagerV1(); SecurityContextManagerV1 *waylandSecurityContextManagerV1();
XXColorManagerV4 *colorManager();
bool waitForWaylandSurface(Window *window); bool waitForWaylandSurface(Window *window);

@ -0,0 +1,229 @@
/*
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kwin_wayland_test.h"
#include "core/colorpipeline.h"
#include "core/output.h"
#include "core/outputbackend.h"
#include "core/outputconfiguration.h"
#include "outputconfigurationstore.h"
#include "pointer_input.h"
#include "tiles/tilemanager.h"
#include "wayland/surface.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/surface.h>
#include <format>
#include "qwayland-xx-color-management-v4.h"
using namespace std::chrono_literals;
namespace KWin
{
static const QString s_socketName = QStringLiteral("wayland_test_color_management-0");
class ImageDescription : public QObject, public QtWayland::xx_image_description_v4
{
Q_OBJECT
public:
explicit ImageDescription(::xx_image_description_v4 *descr)
: QtWayland::xx_image_description_v4(descr)
{
}
~ImageDescription() override
{
xx_image_description_v4_destroy(object());
}
void xx_image_description_v4_ready(uint32_t identity) override
{
Q_EMIT ready();
}
Q_SIGNALS:
void ready();
};
class ColorManagementSurface : public QObject, public QtWayland::xx_color_management_surface_v4
{
Q_OBJECT
public:
explicit ColorManagementSurface(::xx_color_management_surface_v4 *obj)
: QtWayland::xx_color_management_surface_v4(obj)
{
}
~ColorManagementSurface() override
{
xx_color_management_surface_v4_destroy(object());
}
};
class ColorManagementTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void init();
void cleanup();
void testSetImageDescription_data();
void testSetImageDescription();
void testUnsupportedPrimaries();
void testNoPrimaries();
void testNoTf();
};
void ColorManagementTest::initTestCase()
{
qRegisterMetaType<Window *>();
QVERIFY(waylandServer()->init(s_socketName));
Test::setOutputConfig({
QRect(0, 0, 1280, 1024),
QRect(1280, 0, 1280, 1024),
});
kwinApp()->start();
const auto outputs = workspace()->outputs();
QCOMPARE(outputs.count(), 2);
QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
}
void ColorManagementTest::init()
{
Test::setOutputConfig({
QRect(0, 0, 1280, 1024),
QRect(1280, 0, 1280, 1024),
});
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::ColorManagement));
workspace()->setActiveOutput(QPoint(640, 512));
input()->pointer()->warp(QPoint(640, 512));
}
void ColorManagementTest::cleanup()
{
Test::destroyWaylandConnection();
}
void ColorManagementTest::testSetImageDescription_data()
{
QTest::addColumn<ColorDescription>("input");
QTest::addColumn<bool>("protocolError");
QTest::addRow("sRGB") << ColorDescription::sRGB << false;
QTest::addRow("rec.2020 PQ") << ColorDescription(NamedColorimetry::BT2020, TransferFunction(TransferFunction::PerceptualQuantizer), 203, 0, 400, 400) << false;
QTest::addRow("scRGB") << ColorDescription(NamedColorimetry::BT709, TransferFunction(TransferFunction::linear, 0, 80), 80, 0, 80, 80) << false;
QTest::addRow("custom") << ColorDescription(NamedColorimetry::BT2020, TransferFunction(TransferFunction::gamma22, 0.05, 400), 203, 0, 400, 400) << false;
QTest::addRow("invalid tf") << ColorDescription(NamedColorimetry::BT2020, TransferFunction(TransferFunction::gamma22, 204, 205), 203, 0, 400, 400) << true;
// TODO this should fail with the wp protocol version
QTest::addRow("invalid HDR metadata") << ColorDescription(NamedColorimetry::BT2020, TransferFunction(TransferFunction::PerceptualQuantizer), 203, 500, 400, 400) << false;
}
void ColorManagementTest::testSetImageDescription()
{
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
QVERIFY(window);
auto cmSurf = std::make_unique<ColorManagementSurface>(Test::colorManager()->get_surface(*surface));
QtWayland::xx_image_description_creator_params_v4 creator(Test::colorManager()->new_parametric_creator());
QFETCH(ColorDescription, input);
creator.set_primaries(std::round(10'000 * input.containerColorimetry().red().toxyY().x),
std::round(10'000 * input.containerColorimetry().red().toxyY().y),
std::round(10'000 * input.containerColorimetry().green().toxyY().x),
std::round(10'000 * input.containerColorimetry().green().toxyY().y),
std::round(10'000 * input.containerColorimetry().blue().toxyY().x),
std::round(10'000 * input.containerColorimetry().blue().toxyY().y),
std::round(10'000 * input.containerColorimetry().white().toxyY().x),
std::round(10'000 * input.containerColorimetry().white().toxyY().y));
switch (input.transferFunction().type) {
case TransferFunction::sRGB:
creator.set_tf_named(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB);
creator.set_luminances(std::round(input.transferFunction().minLuminance * 10'000), std::round(input.transferFunction().maxLuminance), std::round(input.referenceLuminance()));
break;
case TransferFunction::gamma22:
creator.set_tf_named(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_GAMMA22);
creator.set_luminances(std::round(input.transferFunction().minLuminance * 10'000), std::round(input.transferFunction().maxLuminance), std::round(input.referenceLuminance()));
break;
case TransferFunction::linear:
creator.set_tf_named(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
creator.set_luminances(std::round(input.transferFunction().minLuminance * 10'000), std::round(input.transferFunction().maxLuminance), std::round(input.referenceLuminance()));
break;
case TransferFunction::PerceptualQuantizer:
creator.set_tf_named(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ);
creator.set_max_fall(std::round(input.maxAverageLuminance().value_or(0)));
creator.set_max_cll(std::round(input.maxHdrLuminance().value_or(0)));
break;
}
creator.set_mastering_luminance(std::round(input.minLuminance() * 10'000), std::round(input.maxHdrLuminance().value_or(0)));
ImageDescription imageDescr(xx_image_description_creator_params_v4_create(creator.object()));
QFETCH(bool, protocolError);
if (protocolError) {
QSignalSpy error(Test::waylandConnection(), &KWayland::Client::ConnectionThread::errorOccurred);
QVERIFY(error.wait(50ms));
return;
}
QSignalSpy ready(&imageDescr, &ImageDescription::ready);
QVERIFY(ready.wait(50ms));
cmSurf->set_image_description(imageDescr.object(), XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
QSignalSpy colorChange(window->surface(), &SurfaceInterface::colorDescriptionChanged);
QVERIFY(colorChange.wait());
QCOMPARE(window->surface()->colorDescription(), input);
}
void ColorManagementTest::testUnsupportedPrimaries()
{
QtWayland::xx_image_description_creator_params_v4 creator = QtWayland::xx_image_description_creator_params_v4(Test::colorManager()->new_parametric_creator());
creator.set_primaries_named(-1);
xx_image_description_creator_params_v4_create(creator.object());
QSignalSpy error(Test::waylandConnection(), &KWayland::Client::ConnectionThread::errorOccurred);
QVERIFY(error.wait(50ms));
}
void ColorManagementTest::testNoPrimaries()
{
QtWayland::xx_image_description_creator_params_v4 creator = QtWayland::xx_image_description_creator_params_v4(Test::colorManager()->new_parametric_creator());
creator.set_tf_named(XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
xx_image_description_creator_params_v4_create(creator.object());
QSignalSpy error(Test::waylandConnection(), &KWayland::Client::ConnectionThread::errorOccurred);
QVERIFY(error.wait(50ms));
}
void ColorManagementTest::testNoTf()
{
QtWayland::xx_image_description_creator_params_v4 creator = QtWayland::xx_image_description_creator_params_v4(Test::colorManager()->new_parametric_creator());
creator.set_primaries_named(XX_COLOR_MANAGER_V4_PRIMARIES_CIE1931_XYZ);
xx_image_description_creator_params_v4_create(creator.object());
QSignalSpy error(Test::waylandConnection(), &KWayland::Client::ConnectionThread::errorOccurred);
QVERIFY(error.wait(50ms));
}
} // namespace KWin
WAYLANDTEST_MAIN(KWin::ColorManagementTest)
#include "test_colormanagement.moc"

@ -319,6 +319,7 @@ static struct
FakeInput *fakeInput = nullptr; FakeInput *fakeInput = nullptr;
SecurityContextManagerV1 *securityContextManagerV1 = nullptr; SecurityContextManagerV1 *securityContextManagerV1 = nullptr;
XdgWmDialogV1 *xdgWmDialogV1; XdgWmDialogV1 *xdgWmDialogV1;
std::unique_ptr<XXColorManagerV4> colorManager;
} s_waylandConnection; } s_waylandConnection;
MockInputMethod *inputMethod() MockInputMethod *inputMethod()
@ -540,6 +541,11 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
s_waylandConnection.xdgWmDialogV1->init(*registry, name, version); s_waylandConnection.xdgWmDialogV1->init(*registry, name, version);
} }
} }
if (flags & AdditionalWaylandInterface::ColorManagement) {
if (interface == xx_color_manager_v4_interface.name) {
s_waylandConnection.colorManager = std::make_unique<XXColorManagerV4>(*registry, name, version);
}
}
}); });
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced); QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
@ -667,6 +673,7 @@ void destroyWaylandConnection()
s_waylandConnection.securityContextManagerV1 = nullptr; s_waylandConnection.securityContextManagerV1 = nullptr;
delete s_waylandConnection.xdgWmDialogV1; delete s_waylandConnection.xdgWmDialogV1;
s_waylandConnection.xdgWmDialogV1 = nullptr; s_waylandConnection.xdgWmDialogV1 = nullptr;
s_waylandConnection.colorManager.reset();
delete s_waylandConnection.queue; // Must be destroyed last delete s_waylandConnection.queue; // Must be destroyed last
s_waylandConnection.queue = nullptr; s_waylandConnection.queue = nullptr;
@ -783,6 +790,11 @@ SecurityContextManagerV1 *waylandSecurityContextManagerV1()
return s_waylandConnection.securityContextManagerV1; return s_waylandConnection.securityContextManagerV1;
} }
XXColorManagerV4 *colorManager()
{
return s_waylandConnection.colorManager.get();
}
bool waitForWaylandSurface(Window *window) bool waitForWaylandSurface(Window *window)
{ {
if (window->surface()) { if (window->surface()) {
@ -1657,6 +1669,16 @@ bool VirtualInputDevice::isLidSwitch() const
return m_lidSwitch; return m_lidSwitch;
} }
XXColorManagerV4::XXColorManagerV4(::wl_registry *registry, uint32_t id, int version)
: QtWayland::xx_color_manager_v4(registry, id, version)
{
}
XXColorManagerV4::~XXColorManagerV4()
{
xx_color_manager_v4_destroy(object());
}
void keyboardKeyPressed(quint32 key, quint32 time) void keyboardKeyPressed(quint32 key, quint32 time)
{ {
auto virtualKeyboard = static_cast<WaylandTestApplication *>(kwinApp())->virtualKeyboard(); auto virtualKeyboard = static_cast<WaylandTestApplication *>(kwinApp())->virtualKeyboard();

Loading…
Cancel
Save