diff --git a/src/platform.cpp b/src/platform.cpp index 469fabb70b..8464201b07 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -25,11 +25,6 @@ #include "qpainterbackend.h" #include "scene.h" #include "screenedge.h" -#include "screens.h" -#include "wayland/outputchangeset_v2.h" -#include "wayland/outputconfiguration_v2_interface.h" -#include "wayland_server.h" -#include "workspace.h" #include @@ -124,81 +119,6 @@ void Platform::createPlatformCursor(QObject *parent) new InputRedirectionCursor(parent); } -void Platform::requestOutputsChange(KWaylandServer::OutputConfigurationV2Interface *config) -{ - if (!m_supportsOutputChanges) { - qCWarning(KWIN_CORE) << "This backend does not support configuration changes."; - config->setFailed(); - return; - } - - OutputConfiguration cfg; - const auto changes = config->changes(); - for (auto it = changes.begin(); it != changes.end(); it++) { - const KWaylandServer::OutputChangeSetV2 *changeset = it.value(); - auto output = findOutput(it.key()->uuid()); - if (!output) { - qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid(); - continue; - } - - const auto modes = output->modes(); - const auto modeIt = std::find_if(modes.begin(), modes.end(), [&changeset](const auto &mode) { - return mode->size() == changeset->size() && mode->refreshRate() == changeset->refreshRate(); - }); - if (modeIt == modes.end()) { - qCWarning(KWIN_CORE).nospace() << "Could not find mode " << changeset->size() << "@" << changeset->refreshRate() << " for output " << this; - config->setFailed(); - return; - } - - auto props = cfg.changeSet(output); - props->enabled = changeset->enabled(); - props->pos = changeset->position(); - props->scale = changeset->scale(); - props->mode = *modeIt; - props->transform = static_cast(changeset->transform()); - props->overscan = changeset->overscan(); - props->rgbRange = static_cast(changeset->rgbRange()); - props->vrrPolicy = static_cast(changeset->vrrPolicy()); - } - - const auto allOutputs = outputs(); - bool allDisabled = !std::any_of(allOutputs.begin(), allOutputs.end(), [&cfg](const auto &output) { - return cfg.changeSet(output)->enabled; - }); - if (allDisabled) { - qCWarning(KWIN_CORE) << "Disabling all outputs through configuration changes is not allowed"; - config->setFailed(); - return; - } - - if (applyOutputChanges(cfg)) { - if (config->primaryChanged() || !primaryOutput()->isEnabled()) { - auto requestedPrimaryOutput = findOutput(config->primary()->uuid()); - if (requestedPrimaryOutput && requestedPrimaryOutput->isEnabled()) { - setPrimaryOutput(requestedPrimaryOutput); - } else { - Output *defaultPrimaryOutput = nullptr; - const auto candidates = outputs(); - for (Output *output : candidates) { - if (output->isEnabled()) { - defaultPrimaryOutput = output; - break; - } - } - qCWarning(KWIN_CORE) << "Requested invalid primary screen, using" << defaultPrimaryOutput; - setPrimaryOutput(defaultPrimaryOutput); - } - } - Q_EMIT workspace()->screens()->changed(); - config->setApplied(); - } else { - qCDebug(KWIN_CORE) << "Applying config failed"; - config->setFailed(); - } -} - bool Platform::applyOutputChanges(const OutputConfiguration &config) { const auto availableOutputs = outputs(); diff --git a/src/platform.h b/src/platform.h index 320ad72ad5..6adead379d 100644 --- a/src/platform.h +++ b/src/platform.h @@ -21,11 +21,6 @@ class QAction; -namespace KWaylandServer -{ -class OutputConfigurationV2Interface; -} - namespace KWin { @@ -105,15 +100,6 @@ public: */ void setSceneEglGlobalShareContext(EGLContext context); - /** - * Implement this method to receive configuration change requests through KWayland's - * OutputManagement interface. - * - * Base implementation warns that the current backend does not implement this - * functionality. - */ - void requestOutputsChange(KWaylandServer::OutputConfigurationV2Interface *config); - /** * Whether the Platform requires compositing for rendering. * Default implementation returns @c true. If the implementing Platform allows to be used diff --git a/src/wayland/autotests/client/test_wayland_outputmanagement.cpp b/src/wayland/autotests/client/test_wayland_outputmanagement.cpp deleted file mode 100644 index 1056c0a6b2..0000000000 --- a/src/wayland/autotests/client/test_wayland_outputmanagement.cpp +++ /dev/null @@ -1,507 +0,0 @@ -/* - SPDX-FileCopyrightText: 2014 Martin Gräßlin - SPDX-FileCopyrightText: 2015 Sebastian Kügler - - SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL -*/ -// Qt -#include -// KWin -#include "wayland/compositor_interface.h" -#include "wayland/display.h" -#include "wayland/outputconfiguration_interface.h" -#include "wayland/outputdevice_interface.h" -#include "wayland/outputmanagement_interface.h" - -#include "KWayland/Client/connection_thread.h" -#include "KWayland/Client/event_queue.h" -#include "KWayland/Client/output.h" -#include "KWayland/Client/outputconfiguration.h" -#include "KWayland/Client/outputdevice.h" -#include "KWayland/Client/outputmanagement.h" -#include "KWayland/Client/registry.h" - -// Wayland -#include - -using namespace KWayland::Client; -using namespace KWaylandServer; - -class TestWaylandOutputManagement : public QObject -{ - Q_OBJECT -public: - explicit TestWaylandOutputManagement(QObject *parent = nullptr); -private Q_SLOTS: - void init(); - void cleanup(); - void createConfig(); - - void testBasicMemoryManagement(); - void testMultipleSettings(); - void testConfigFailed(); - void testApplied(); - void testFailed(); - - void testExampleConfig(); - void testScale(); - - void testRemoval(); - -private: - void createOutputDevices(); - void testEnable(); - void applyPendingChanges(KWaylandServer::OutputConfigurationInterface *configurationInterface); - - KWaylandServer::Display *m_display; - KWaylandServer::OutputManagementInterface *m_outputManagementInterface; - QList m_serverOutputs; - - KWayland::Client::Registry *m_registry = nullptr; - KWayland::Client::OutputDevice *m_outputDevice = nullptr; - KWayland::Client::OutputManagement *m_outputManagement = nullptr; - KWayland::Client::OutputConfiguration *m_outputConfiguration = nullptr; - QList m_clientOutputs; - QList m_modes; - - KWayland::Client::ConnectionThread *m_connection = nullptr; - KWayland::Client::EventQueue *m_queue = nullptr; - QThread *m_thread; - - QSignalSpy *m_announcedSpy; - QSignalSpy *m_omSpy; -}; - -static const QString s_socketName = QStringLiteral("kwin-test-wayland-output-0"); - -TestWaylandOutputManagement::TestWaylandOutputManagement(QObject *parent) - : QObject(parent) - , m_display(nullptr) - , m_outputManagementInterface(nullptr) - , m_connection(nullptr) - , m_queue(nullptr) - , m_thread(nullptr) - , m_announcedSpy(nullptr) -{ - qRegisterMetaType(); -} - -void TestWaylandOutputManagement::init() -{ - using namespace KWaylandServer; - delete m_display; - m_display = new KWaylandServer::Display(this); - m_display->addSocketName(s_socketName); - m_display->start(); - QVERIFY(m_display->isRunning()); - - new CompositorInterface(m_display, this); - auto outputDeviceInterface = new OutputDeviceInterface(m_display, this); - - OutputDeviceInterface::Mode m0; - m0.id = 0; - m0.size = QSize(800, 600); - m0.flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); - outputDeviceInterface->addMode(m0); - - OutputDeviceInterface::Mode m1; - m1.id = 1; - m1.size = QSize(1024, 768); - outputDeviceInterface->addMode(m1); - - OutputDeviceInterface::Mode m2; - m2.id = 2; - m2.size = QSize(1280, 1024); - m2.refreshRate = 90000; - outputDeviceInterface->addMode(m2); - - OutputDeviceInterface::Mode m3; - m3.id = 3; - m3.size = QSize(1920, 1080); - m3.flags = OutputDeviceInterface::ModeFlags(); - m3.refreshRate = 100000; - outputDeviceInterface->addMode(m3); - - m_modes << m0 << m1 << m2 << m3; - - outputDeviceInterface->setCurrentMode(1); - outputDeviceInterface->setGlobalPosition(QPoint(0, 1920)); - m_serverOutputs << outputDeviceInterface; - - m_outputManagementInterface = new OutputManagementInterface(m_display, this); - - // setup connection - 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()); - - m_queue = new KWayland::Client::EventQueue(this); - QVERIFY(!m_queue->isValid()); - m_queue->setup(m_connection); - QVERIFY(m_queue->isValid()); - - m_registry = new Registry(); - - m_announcedSpy = new QSignalSpy(m_registry, &KWayland::Client::Registry::outputManagementAnnounced); - m_omSpy = new QSignalSpy(m_registry, &KWayland::Client::Registry::outputDeviceAnnounced); - - QVERIFY(m_announcedSpy->isValid()); - QVERIFY(m_omSpy->isValid()); - - m_registry->create(m_connection->display()); - QVERIFY(m_registry->isValid()); - m_registry->setEventQueue(m_queue); - m_registry->setup(); - wl_display_flush(m_connection->display()); - - QVERIFY(m_announcedSpy->wait()); - QCOMPARE(m_announcedSpy->count(), 1); - - m_outputManagement = m_registry->createOutputManagement(m_announcedSpy->first().first().value(), m_announcedSpy->first().last().value()); - createOutputDevices(); -} - -void TestWaylandOutputManagement::cleanup() -{ - if (m_outputConfiguration) { - delete m_outputConfiguration; - m_outputConfiguration = nullptr; - } - if (m_outputManagement) { - delete m_outputManagement; - m_outputManagement = nullptr; - } - if (m_registry) { - delete m_registry; - m_registry = nullptr; - } - if (m_queue) { - delete m_queue; - m_queue = nullptr; - } - if (m_connection) { - m_connection->deleteLater(); - m_connection = nullptr; - } - if (m_thread) { - m_thread->quit(); - m_thread->wait(); - delete m_thread; - m_thread = nullptr; - } - - delete m_display; - m_display = nullptr; - m_serverOutputs.clear(); - m_clientOutputs.clear(); - - // these are the children of the display - m_outputManagementInterface = nullptr; -} - -void TestWaylandOutputManagement::applyPendingChanges(KWaylandServer::OutputConfigurationInterface *configurationInterface) -{ - auto changes = configurationInterface->changes(); - for (auto outputdevice : changes.keys()) { - auto c = changes[outputdevice]; - if (c->enabledChanged()) { - outputdevice->setEnabled(c->enabled()); - } - if (c->modeChanged()) { - outputdevice->setCurrentMode(c->mode()); - } - if (c->transformChanged()) { - outputdevice->setTransform(c->transform()); - } - if (c->positionChanged()) { - outputdevice->setGlobalPosition(c->position()); - } - if (c->scaleChanged()) { - outputdevice->setScaleF(c->scaleF()); - } - if (c->colorCurvesChanged()) { - outputdevice->setColorCurves(c->colorCurves()); - } - } -} - -void TestWaylandOutputManagement::createOutputDevices() -{ - QCOMPARE(m_omSpy->count(), 1); - QCOMPARE(m_registry->interfaces(KWayland::Client::Registry::Interface::OutputDevice).count(), m_serverOutputs.count()); - - auto output = new KWayland::Client::OutputDevice(); - QVERIFY(!output->isValid()); - QCOMPARE(output->geometry(), QRect()); - QCOMPARE(output->globalPosition(), QPoint()); - QCOMPARE(output->manufacturer(), QString()); - QCOMPARE(output->model(), QString()); - QCOMPARE(output->physicalSize(), QSize()); - QCOMPARE(output->pixelSize(), QSize()); - QCOMPARE(output->refreshRate(), 0); - QCOMPARE(output->scaleF(), 1.0); - QCOMPARE(output->colorCurves().red, QVector()); - QCOMPARE(output->colorCurves().green, QVector()); - QCOMPARE(output->colorCurves().blue, QVector()); - QCOMPARE(output->subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown); - QCOMPARE(output->transform(), KWayland::Client::OutputDevice::Transform::Normal); - QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); - QCOMPARE(output->edid(), QByteArray()); - QCOMPARE(output->uuid(), QString()); - - QSignalSpy outputChanged(output, &KWayland::Client::OutputDevice::changed); - QVERIFY(outputChanged.isValid()); - - output->setup(m_registry->bindOutputDevice(m_omSpy->first().first().value(), m_omSpy->first().last().value())); - wl_display_flush(m_connection->display()); - - QVERIFY(outputChanged.wait()); - QCOMPARE(output->globalPosition(), QPoint(0, 1920)); - QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); - - m_clientOutputs << output; - m_outputDevice = output; - - QVERIFY(m_outputManagement->isValid()); -} - -void TestWaylandOutputManagement::testBasicMemoryManagement() -{ - createConfig(); - - QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); - KWaylandServer::OutputConfigurationInterface *configurationInterface = nullptr; - connect(m_outputManagementInterface, - &OutputManagementInterface::configurationChangeRequested, - [=, &configurationInterface](KWaylandServer::OutputConfigurationInterface *c) { - configurationInterface = c; - }); - m_outputConfiguration->apply(); - - QVERIFY(serverApplySpy.wait()); - QVERIFY(configurationInterface); - QSignalSpy interfaceDeletedSpy(configurationInterface, &QObject::destroyed); - - delete m_outputConfiguration; - m_outputConfiguration = nullptr; - QVERIFY(interfaceDeletedSpy.wait()); -} - -void TestWaylandOutputManagement::testRemoval() -{ - QSignalSpy outputManagementRemovedSpy(m_registry, &KWayland::Client::Registry::outputManagementRemoved); - QVERIFY(outputManagementRemovedSpy.isValid()); - - delete m_outputManagementInterface; - m_outputManagementInterface = nullptr; - QVERIFY(outputManagementRemovedSpy.wait(200)); - QCOMPARE(outputManagementRemovedSpy.first().first(), m_announcedSpy->first().first()); - QVERIFY(!m_registry->hasInterface(KWayland::Client::Registry::Interface::OutputManagement)); - QVERIFY(m_registry->interfaces(KWayland::Client::Registry::Interface::OutputManagement).isEmpty()); -} - -void TestWaylandOutputManagement::createConfig() -{ - m_outputConfiguration = m_outputManagement->createConfiguration(); -} - -void TestWaylandOutputManagement::testApplied() -{ - createConfig(); - QVERIFY(m_outputConfiguration->isValid()); - QSignalSpy appliedSpy(m_outputConfiguration, &KWayland::Client::OutputConfiguration::applied); - - connect(m_outputManagementInterface, - &OutputManagementInterface::configurationChangeRequested, - [=](KWaylandServer::OutputConfigurationInterface *configurationInterface) { - configurationInterface->setApplied(); - }); - m_outputConfiguration->apply(); - QVERIFY(appliedSpy.wait(200)); - QCOMPARE(appliedSpy.count(), 1); -} - -void TestWaylandOutputManagement::testFailed() -{ - createConfig(); - QVERIFY(m_outputConfiguration->isValid()); - QSignalSpy failedSpy(m_outputConfiguration, &KWayland::Client::OutputConfiguration::failed); - - connect(m_outputManagementInterface, - &OutputManagementInterface::configurationChangeRequested, - [=](KWaylandServer::OutputConfigurationInterface *configurationInterface) { - configurationInterface->setFailed(); - }); - m_outputConfiguration->apply(); - QVERIFY(failedSpy.wait(200)); - QCOMPARE(failedSpy.count(), 1); -} - -void TestWaylandOutputManagement::testEnable() -{ - createConfig(); - auto config = m_outputConfiguration; - QVERIFY(config->isValid()); - - KWayland::Client::OutputDevice *output = m_clientOutputs.first(); - QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); - - QSignalSpy enabledChanged(output, &KWayland::Client::OutputDevice::enabledChanged); - QVERIFY(enabledChanged.isValid()); - - config->setEnabled(output, OutputDevice::Enablement::Disabled); - - QVERIFY(!enabledChanged.wait(200)); - - QCOMPARE(enabledChanged.count(), 0); - - // Reset - config->setEnabled(output, OutputDevice::Enablement::Disabled); - config->apply(); -} - -void TestWaylandOutputManagement::testMultipleSettings() -{ - createConfig(); - auto config = m_outputConfiguration; - QVERIFY(config->isValid()); - - KWayland::Client::OutputDevice *output = m_clientOutputs.first(); - QSignalSpy outputChangedSpy(output, &KWayland::Client::OutputDevice::changed); - - KWaylandServer::OutputConfigurationInterface *configurationInterface; - connect(m_outputManagementInterface, - &OutputManagementInterface::configurationChangeRequested, - [=, &configurationInterface](KWaylandServer::OutputConfigurationInterface *c) { - applyPendingChanges(c); - configurationInterface = c; - }); - QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); - QVERIFY(serverApplySpy.isValid()); - - config->setMode(output, m_modes.first().id); - config->setTransform(output, OutputDevice::Transform::Rotated90); - config->setPosition(output, QPoint(13, 37)); - config->setScaleF(output, 2.0); - const auto zeroVector = QVector(256, 0); - config->setColorCurves(output, zeroVector, zeroVector, zeroVector); - config->setEnabled(output, OutputDevice::Enablement::Disabled); - config->apply(); - - QVERIFY(serverApplySpy.wait(200)); - QCOMPARE(serverApplySpy.count(), 1); - - configurationInterface->setApplied(); - - QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); - QVERIFY(configAppliedSpy.isValid()); - QVERIFY(configAppliedSpy.wait(200)); - QCOMPARE(configAppliedSpy.count(), 1); - QCOMPARE(outputChangedSpy.count(), 5); - - config->setMode(output, m_modes.at(1).id); - config->setTransform(output, OutputDevice::Transform::Normal); - config->setPosition(output, QPoint(0, 1920)); - config->setScaleF(output, 1.0); - const auto oneVector = QVector(256, 1); - config->setColorCurves(output, oneVector, oneVector, oneVector); - config->setEnabled(output, OutputDevice::Enablement::Enabled); - config->apply(); - - QVERIFY(serverApplySpy.wait(200)); - QCOMPARE(serverApplySpy.count(), 2); - - configurationInterface->setApplied(); - - QVERIFY(configAppliedSpy.wait(200)); - QCOMPARE(configAppliedSpy.count(), 2); - QCOMPARE(outputChangedSpy.count(), 10); -} - -void TestWaylandOutputManagement::testConfigFailed() -{ - createConfig(); - auto config = m_outputConfiguration; - KWayland::Client::OutputDevice *output = m_clientOutputs.first(); - - QVERIFY(config->isValid()); - QVERIFY(output->isValid()); - - QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); - QVERIFY(serverApplySpy.isValid()); - QSignalSpy outputChangedSpy(output, &KWayland::Client::OutputDevice::changed); - QVERIFY(outputChangedSpy.isValid()); - QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); - QVERIFY(configAppliedSpy.isValid()); - QSignalSpy configFailedSpy(config, &KWayland::Client::OutputConfiguration::failed); - QVERIFY(configFailedSpy.isValid()); - - config->setMode(output, m_modes.last().id); - config->setTransform(output, OutputDevice::Transform::Normal); - config->setPosition(output, QPoint(-1, -1)); - - config->apply(); - - connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWaylandServer::OutputConfigurationInterface *c) { - c->setFailed(); - }); - - QVERIFY(serverApplySpy.wait(200)); - - // Artificially make the server fail to apply the settings - // Make sure the applied signal never comes, and that failed has been received - QVERIFY(!configAppliedSpy.wait(200)); - QCOMPARE(configFailedSpy.count(), 1); - QCOMPARE(configAppliedSpy.count(), 0); -} - -void TestWaylandOutputManagement::testExampleConfig() -{ - createConfig(); - auto config = m_outputConfiguration; - KWayland::Client::OutputDevice *output = m_clientOutputs.first(); - - config->setMode(output, m_clientOutputs.first()->modes().last().id); - config->setTransform(output, OutputDevice::Transform::Normal); - config->setPosition(output, QPoint(-1, -1)); - - QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); - connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWaylandServer::OutputConfigurationInterface *c) { - c->setApplied(); - }); - config->apply(); - - QVERIFY(configAppliedSpy.isValid()); - QVERIFY(configAppliedSpy.wait(200)); -} - -void TestWaylandOutputManagement::testScale() -{ - createConfig(); - - auto config = m_outputConfiguration; - KWayland::Client::OutputDevice *output = m_clientOutputs.first(); - - config->setScaleF(output, 2.3); - config->apply(); - - QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); - connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWaylandServer::OutputConfigurationInterface *c) { - applyPendingChanges(c); - c->setApplied(); - }); - QVERIFY(configAppliedSpy.isValid()); - QVERIFY(configAppliedSpy.wait(200)); - - QCOMPARE(wl_fixed_from_double(output->scaleF()), wl_fixed_from_double(2.3)); -} - -QTEST_GUILESS_MAIN(TestWaylandOutputManagement) -#include "test_wayland_outputmanagement.moc" diff --git a/src/wayland/autotests/server/test_display.cpp b/src/wayland/autotests/server/test_display.cpp index 450f25f4a9..4aa4e1871e 100644 --- a/src/wayland/autotests/server/test_display.cpp +++ b/src/wayland/autotests/server/test_display.cpp @@ -9,7 +9,6 @@ #include "wayland/clientconnection.h" #include "wayland/display.h" #include "wayland/output_interface.h" -#include "wayland/outputmanagement_v2_interface.h" // Wayland #include // system @@ -28,7 +27,6 @@ private Q_SLOTS: void testAddRemoveOutput(); void testClientConnection(); void testConnectNoSocket(); - void testOutputManagement(); void testAutoSocketName(); }; @@ -178,14 +176,6 @@ void TestWaylandServerDisplay::testConnectNoSocket() close(sv[1]); } -void TestWaylandServerDisplay::testOutputManagement() -{ - KWaylandServer::Display display; - display.addSocketName("kwayland-test-0"); - display.start(); - new OutputManagementV2Interface(&display, this); -} - void TestWaylandServerDisplay::testAutoSocketName() { QTemporaryDir runtimeDir; diff --git a/src/wayland/outputchangeset_v2.h b/src/wayland/outputchangeset_v2.h index 41e0b833ad..497f274143 100644 --- a/src/wayland/outputchangeset_v2.h +++ b/src/wayland/outputchangeset_v2.h @@ -106,7 +106,7 @@ public: OutputDeviceV2Interface::RgbRange rgbRange() const; private: - friend class OutputConfigurationV2InterfacePrivate; + friend class OutputConfigurationV2Interface; explicit OutputChangeSetV2(OutputDeviceV2Interface *outputdevice, QObject *parent = nullptr); std::unique_ptr d; diff --git a/src/wayland/outputconfiguration_v2_interface.cpp b/src/wayland/outputconfiguration_v2_interface.cpp index d99817ce1d..0423d01155 100644 --- a/src/wayland/outputconfiguration_v2_interface.cpp +++ b/src/wayland/outputconfiguration_v2_interface.cpp @@ -11,49 +11,30 @@ #include "outputdevice_v2_interface.h" #include "utils/common.h" +#include "main.h" +#include "outputconfiguration.h" +#include "platform.h" +#include "screens.h" +#include "workspace.h" + #include "qwayland-server-kde-output-device-v2.h" -#include "qwayland-server-kde-output-management-v2.h" -#include - -#include +using namespace KWin; namespace KWaylandServer { -class OutputConfigurationV2InterfacePrivate : public QtWaylandServer::kde_output_configuration_v2 + +OutputConfigurationV2Interface::OutputConfigurationV2Interface(wl_resource *resource) + : QtWaylandServer::kde_output_configuration_v2(resource) { -public: - OutputConfigurationV2InterfacePrivate(OutputConfigurationV2Interface *q, OutputManagementV2Interface *outputManagement, wl_resource *resource); +} - void sendApplied(); - void sendFailed(); - void emitConfigurationChangeRequested() const; - void clearPendingChanges(); +OutputConfigurationV2Interface::~OutputConfigurationV2Interface() +{ + qDeleteAll(changes.begin(), changes.end()); +} - bool hasPendingChanges(OutputDeviceV2Interface *outputdevice) const; - OutputChangeSetV2 *pendingChanges(OutputDeviceV2Interface *outputdevice); - - OutputManagementV2Interface *outputManagement; - QHash changes; - std::optional primaryOutput; - OutputConfigurationV2Interface *q; - -protected: - void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override; - void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override; - void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override; - void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override; - void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override; - void kde_output_configuration_v2_apply(Resource *resource) override; - void kde_output_configuration_v2_destroy(Resource *resource) override; - void kde_output_configuration_v2_destroy_resource(Resource *resource) override; - void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override; - void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override; - void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override; - void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override; -}; - -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) +void OutputConfigurationV2Interface::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) { Q_UNUSED(resource) @@ -61,7 +42,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_enable(R pendingChanges(output)->d->enabled = enable == 1; } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_mode(Resource *resource, wl_resource *outputdevice, wl_resource *modeResource) +void OutputConfigurationV2Interface::kde_output_configuration_v2_mode(Resource *resource, wl_resource *outputdevice, wl_resource *modeResource) { Q_UNUSED(resource) OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); @@ -74,7 +55,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_mode(Res pendingChanges(output)->d->refreshRate = mode->refreshRate(); } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) +void OutputConfigurationV2Interface::kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) { Q_UNUSED(resource) auto toTransform = [transform]() { @@ -103,7 +84,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_transfor pendingChanges(output)->d->transform = _transform; } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) +void OutputConfigurationV2Interface::kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) { Q_UNUSED(resource) auto _pos = QPoint(x, y); @@ -111,7 +92,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_position pendingChanges(output)->d->position = _pos; } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) +void OutputConfigurationV2Interface::kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) { Q_UNUSED(resource) const qreal doubleScale = wl_fixed_to_double(scale); @@ -125,13 +106,82 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_scale(Re pendingChanges(output)->d->scale = doubleScale; } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_apply(Resource *resource) +void OutputConfigurationV2Interface::kde_output_configuration_v2_apply(Resource *resource) { - Q_UNUSED(resource) - emitConfigurationChangeRequested(); + if (applied) { + wl_resource_post_error(resource->handle, 0, "an output configuration can be applied only once"); + return; + } + + applied = true; + + OutputConfiguration cfg; + for (auto it = changes.constBegin(); it != changes.constEnd(); ++it) { + const KWaylandServer::OutputChangeSetV2 *changeset = it.value(); + auto output = kwinApp()->platform()->findOutput(it.key()->uuid()); + if (!output) { + qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid(); + continue; + } + + const auto modes = output->modes(); + const auto modeIt = std::find_if(modes.begin(), modes.end(), [&changeset](const auto &mode) { + return mode->size() == changeset->size() && mode->refreshRate() == changeset->refreshRate(); + }); + if (modeIt == modes.end()) { + qCWarning(KWIN_CORE).nospace() << "Could not find mode " << changeset->size() << "@" << changeset->refreshRate() << " for output " << this; + send_failed(); + return; + } + + auto props = cfg.changeSet(output); + props->enabled = changeset->enabled(); + props->pos = changeset->position(); + props->scale = changeset->scale(); + props->mode = *modeIt; + props->transform = static_cast(changeset->transform()); + props->overscan = changeset->overscan(); + props->rgbRange = static_cast(changeset->rgbRange()); + props->vrrPolicy = static_cast(changeset->vrrPolicy()); + } + + const auto allOutputs = kwinApp()->platform()->outputs(); + bool allDisabled = !std::any_of(allOutputs.begin(), allOutputs.end(), [&cfg](const auto &output) { + return cfg.changeSet(output)->enabled; + }); + if (allDisabled) { + qCWarning(KWIN_CORE) << "Disabling all outputs through configuration changes is not allowed"; + send_failed(); + return; + } + + if (kwinApp()->platform()->applyOutputChanges(cfg)) { + if (primaryOutput.has_value() || !kwinApp()->platform()->primaryOutput()->isEnabled()) { + auto requestedPrimaryOutput = kwinApp()->platform()->findOutput((*primaryOutput)->uuid()); + if (requestedPrimaryOutput && requestedPrimaryOutput->isEnabled()) { + kwinApp()->platform()->setPrimaryOutput(requestedPrimaryOutput); + } else { + Output *defaultPrimaryOutput = nullptr; + const auto candidates = kwinApp()->platform()->outputs(); + for (Output *output : candidates) { + if (output->isEnabled()) { + defaultPrimaryOutput = output; + break; + } + } + qCWarning(KWIN_CORE) << "Requested invalid primary screen, using" << defaultPrimaryOutput; + kwinApp()->platform()->setPrimaryOutput(defaultPrimaryOutput); + } + } + Q_EMIT workspace()->screens()->changed(); + send_applied(); + } else { + qCDebug(KWIN_CORE) << "Applying config failed"; + send_failed(); + } } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) +void OutputConfigurationV2Interface::kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) { Q_UNUSED(resource) if (overscan > 100) { @@ -142,7 +192,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_overscan pendingChanges(output)->d->overscan = overscan; } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_vrr_policy(Resource *resource, wl_resource *outputdevice, uint32_t policy) +void OutputConfigurationV2Interface::kde_output_configuration_v2_set_vrr_policy(Resource *resource, wl_resource *outputdevice, uint32_t policy) { Q_UNUSED(resource) if (policy > static_cast(OutputDeviceV2Interface::VrrPolicy::Automatic)) { @@ -153,7 +203,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_vrr_ pendingChanges(output)->d->vrrPolicy = static_cast(policy); } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) +void OutputConfigurationV2Interface::kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) { Q_UNUSED(resource) if (rgbRange > static_cast(OutputDeviceV2Interface::RgbRange::Limited)) { @@ -164,107 +214,30 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_rgb_ pendingChanges(output)->d->rgbRange = static_cast(rgbRange); } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) +void OutputConfigurationV2Interface::kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) { Q_UNUSED(resource); primaryOutput = OutputDeviceV2Interface::get(output); } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_destroy(Resource *resource) +void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy(Resource *resource) { wl_resource_destroy(resource->handle); } -void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_destroy_resource(Resource *resource) +void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy_resource(Resource *resource) { Q_UNUSED(resource) - delete q; + delete this; } -void OutputConfigurationV2InterfacePrivate::emitConfigurationChangeRequested() const -{ - Q_EMIT outputManagement->configurationChangeRequested(q); -} - -OutputConfigurationV2InterfacePrivate::OutputConfigurationV2InterfacePrivate(OutputConfigurationV2Interface *q, OutputManagementV2Interface *outputManagement, wl_resource *resource) - : QtWaylandServer::kde_output_configuration_v2(resource) - , outputManagement(outputManagement) - , q(q) -{ -} - -QHash OutputConfigurationV2Interface::changes() const -{ - return d->changes; -} - -bool OutputConfigurationV2Interface::primaryChanged() const -{ - return d->primaryOutput.has_value(); -} - -OutputDeviceV2Interface *OutputConfigurationV2Interface::primary() const -{ - Q_ASSERT(d->primaryOutput.has_value()); - return *d->primaryOutput; -} - -void OutputConfigurationV2Interface::setApplied() -{ - d->clearPendingChanges(); - d->sendApplied(); -} - -void OutputConfigurationV2InterfacePrivate::sendApplied() -{ - send_applied(); -} - -void OutputConfigurationV2Interface::setFailed() -{ - d->clearPendingChanges(); - d->sendFailed(); -} - -void OutputConfigurationV2InterfacePrivate::sendFailed() -{ - send_failed(); -} - -OutputChangeSetV2 *OutputConfigurationV2InterfacePrivate::pendingChanges(OutputDeviceV2Interface *outputdevice) +OutputChangeSetV2 *OutputConfigurationV2Interface::pendingChanges(OutputDeviceV2Interface *outputdevice) { auto &change = changes[outputdevice]; if (!change) { - change = new OutputChangeSetV2(outputdevice, q); + change = new OutputChangeSetV2(outputdevice); } return change; } -bool OutputConfigurationV2InterfacePrivate::hasPendingChanges(OutputDeviceV2Interface *outputdevice) const -{ - auto it = changes.constFind(outputdevice); - if (it == changes.constEnd()) { - return false; - } - auto c = *it; - return c->enabledChanged() || c->sizeChanged() || c->refreshRateChanged() || c->transformChanged() || c->positionChanged() || c->scaleChanged(); -} - -void OutputConfigurationV2InterfacePrivate::clearPendingChanges() -{ - qDeleteAll(changes.begin(), changes.end()); - changes.clear(); -} - -OutputConfigurationV2Interface::OutputConfigurationV2Interface(OutputManagementV2Interface *parent, wl_resource *resource) - : QObject() - , d(new OutputConfigurationV2InterfacePrivate(this, parent, resource)) -{ -} - -OutputConfigurationV2Interface::~OutputConfigurationV2Interface() -{ - d->clearPendingChanges(); -} - } diff --git a/src/wayland/outputconfiguration_v2_interface.h b/src/wayland/outputconfiguration_v2_interface.h index c3f9d32c2e..484624bcfc 100644 --- a/src/wayland/outputconfiguration_v2_interface.h +++ b/src/wayland/outputconfiguration_v2_interface.h @@ -6,88 +6,44 @@ */ #pragma once -#include "kwin_export.h" +#include "qwayland-server-kde-output-management-v2.h" #include "outputchangeset_v2.h" #include "outputdevice_v2_interface.h" #include "outputmanagement_v2_interface.h" +#include + +#include + namespace KWaylandServer { -class OutputConfigurationV2InterfacePrivate; - -/** @class OutputConfigurationInterface - * - * Holds a new configuration for the outputs. - * - * The overall mechanism is to get a new OutputConfiguration from the OutputManagement global and - * apply changes through the OutputConfiguration::set* calls. When all changes are set, the client - * calls apply, which asks the server to look at the changes and apply them. The server will then - * signal back whether the changes have been applied successfully (@c setApplied()) or were rejected - * or failed to apply (@c setFailed()). - * - * Once the client has called applied, the OutputManagementInterface send the configuration object - * to the compositor through the OutputManagement::configurationChangeRequested(OutputConfiguration*) - * signal, the compositor can then decide what to do with the changes. - * - * These KWayland classes will not apply changes to the OutputDevices, this is the compositor's - * task. As such, the configuration set through this interface can be seen as a hint what the - * compositor should set up, but whether or not the compositor does it (based on hardware or - * rendering policies, for example), is up to the compositor. The mode setting is passed on to - * the DRM subsystem through the compositor. The compositor also saves this configuration and reads - * it on startup, this interface is not involved in that process. - * - * @see OutputManagementInterface - * @see OutputConfiguration - */ -class KWIN_EXPORT OutputConfigurationV2Interface : public QObject +class OutputConfigurationV2Interface : public QtWaylandServer::kde_output_configuration_v2 { - Q_OBJECT public: + explicit OutputConfigurationV2Interface(wl_resource *resource); ~OutputConfigurationV2Interface() override; - /** - * Accessor for the changes made to OutputDevices. The data returned from this call - * will be deleted by the OutputConfigurationInterface when - * OutputManagementInterface::setApplied() or OutputManagementInterface::setFailed() - * is called, and on destruction of the OutputConfigurationInterface, so make sure you - * do not keep these pointers around. - * @returns A QHash of ChangeSets per outputdevice. - * @see ChangeSet - * @see OutputDeviceInterface - * @see OutputManagement - */ - QHash changes() const; + OutputChangeSetV2 *pendingChanges(OutputDeviceV2Interface *outputdevice); - bool primaryChanged() const; - OutputDeviceV2Interface *primary() const; + bool applied = false; + QHash changes; + std::optional primaryOutput; -public Q_SLOTS: - /** - * Called by the compositor once the changes have successfully been applied. - * The compositor is responsible for updating the OutputDevices. After having - * done so, calling this function sends applied() through the client. - * @see setFailed - * @see OutputConfiguration::applied - */ - void setApplied(); - /** - * Called by the compositor when the changes as a whole are rejected or - * failed to apply. This function results in the client OutputConfiguration emitting - * failed(). - * @see setApplied - * @see OutputConfiguration::failed - */ - void setFailed(); - -private: - explicit OutputConfigurationV2Interface(OutputManagementV2Interface *parent, wl_resource *resource); - friend class OutputManagementV2InterfacePrivate; - - std::unique_ptr d; +protected: + void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override; + void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override; + void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override; + void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override; + void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override; + void kde_output_configuration_v2_apply(Resource *resource) override; + void kde_output_configuration_v2_destroy(Resource *resource) override; + void kde_output_configuration_v2_destroy_resource(Resource *resource) override; + void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override; + void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override; + void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override; + void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override; }; } - -Q_DECLARE_METATYPE(KWaylandServer::OutputConfigurationV2Interface *) diff --git a/src/wayland/outputmanagement_v2_interface.cpp b/src/wayland/outputmanagement_v2_interface.cpp index aeb809625d..c774a8a8aa 100644 --- a/src/wayland/outputmanagement_v2_interface.cpp +++ b/src/wayland/outputmanagement_v2_interface.cpp @@ -42,7 +42,7 @@ void OutputManagementV2InterfacePrivate::kde_output_management_v2_create_configu wl_client_post_no_memory(resource->client()); return; } - new OutputConfigurationV2Interface(q, config_resource); + new OutputConfigurationV2Interface(config_resource); } OutputManagementV2Interface::OutputManagementV2Interface(Display *display, QObject *parent) diff --git a/src/wayland/outputmanagement_v2_interface.h b/src/wayland/outputmanagement_v2_interface.h index a0cd307c35..8663913e57 100644 --- a/src/wayland/outputmanagement_v2_interface.h +++ b/src/wayland/outputmanagement_v2_interface.h @@ -15,7 +15,6 @@ namespace KWaylandServer class Display; class OutputManagementV2InterfacePrivate; -class OutputConfigurationV2Interface; /** * @class OutputManagementInterface @@ -38,22 +37,6 @@ public: explicit OutputManagementV2Interface(Display *display, QObject *parent = nullptr); ~OutputManagementV2Interface() override; -Q_SIGNALS: - /** - * Emitted after the client has requested an OutputConfiguration to be applied. - * through OutputConfiguration::apply. The compositor can use this object to get - * notified when the new configuration is set up, and it should be applied to the - * Wayland server's OutputInterfaces. - * - * @param config The OutputConfigurationInterface corresponding to the client that - * called apply(). - * @see OutputConfiguration::apply - * @see OutputConfigurationInterface - * @see OutputDeviceInterface - * @see OutputInterface - */ - void configurationChangeRequested(KWaylandServer::OutputConfigurationV2Interface *configurationInterface); - private: std::unique_ptr d; }; diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp index e8779a738a..32101c8d9a 100644 --- a/src/wayland_server.cpp +++ b/src/wayland_server.cpp @@ -465,9 +465,6 @@ bool WaylandServer::init(InitializationFlags flags) }); m_outputManagement = new OutputManagementV2Interface(m_display, m_display); - connect(m_outputManagement, &OutputManagementV2Interface::configurationChangeRequested, this, [](KWaylandServer::OutputConfigurationV2Interface *config) { - kwinApp()->platform()->requestOutputsChange(config); - }); m_primary = new PrimaryOutputV1Interface(m_display, m_display); m_xdgOutputManagerV1 = new XdgOutputManagerV1Interface(m_display, m_display);