wayland: Move output-management-v2 implementation in wayland/
Since both output-management-v2 protocol implementation and the rest of kwin live in the same place and the fact that kde-output-management-v2 is very plasma specific, we can move Platform::requestOutputsChange() to the implementation of kde-output-management-v2 protocol, it simplifies the code a bit and improve code encapsulation. In order to further simplify kde-output-management-v2 protocol, this change alters the behavior of the protocol so an output configuration can be applied only once, which is a very reasonable behavior.
This commit is contained in:
parent
a9267bdcdc
commit
5f15f3b47c
10 changed files with 127 additions and 829 deletions
|
@ -25,11 +25,6 @@
|
||||||
#include "qpainterbackend.h"
|
#include "qpainterbackend.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
#include "screenedge.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 <KCoreAddons>
|
#include <KCoreAddons>
|
||||||
|
|
||||||
|
@ -124,81 +119,6 @@ void Platform::createPlatformCursor(QObject *parent)
|
||||||
new InputRedirectionCursor(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<Output::Transform>(changeset->transform());
|
|
||||||
props->overscan = changeset->overscan();
|
|
||||||
props->rgbRange = static_cast<Output::RgbRange>(changeset->rgbRange());
|
|
||||||
props->vrrPolicy = static_cast<RenderLoop::VrrPolicy>(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)
|
bool Platform::applyOutputChanges(const OutputConfiguration &config)
|
||||||
{
|
{
|
||||||
const auto availableOutputs = outputs();
|
const auto availableOutputs = outputs();
|
||||||
|
|
|
@ -21,11 +21,6 @@
|
||||||
|
|
||||||
class QAction;
|
class QAction;
|
||||||
|
|
||||||
namespace KWaylandServer
|
|
||||||
{
|
|
||||||
class OutputConfigurationV2Interface;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -105,15 +100,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void setSceneEglGlobalShareContext(EGLContext context);
|
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.
|
* Whether the Platform requires compositing for rendering.
|
||||||
* Default implementation returns @c true. If the implementing Platform allows to be used
|
* Default implementation returns @c true. If the implementing Platform allows to be used
|
||||||
|
|
|
@ -1,507 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
|
||||||
SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
|
||||||
*/
|
|
||||||
// Qt
|
|
||||||
#include <QtTest>
|
|
||||||
// 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 <wayland-client-protocol.h>
|
|
||||||
|
|
||||||
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<KWaylandServer::OutputDeviceInterface *> 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<KWayland::Client::OutputDevice *> m_clientOutputs;
|
|
||||||
QList<KWaylandServer::OutputDeviceInterface::Mode> 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<KWaylandServer::OutputConfigurationInterface *>();
|
|
||||||
}
|
|
||||||
|
|
||||||
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<quint32>(), m_announcedSpy->first().last().value<quint32>());
|
|
||||||
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<quint16>());
|
|
||||||
QCOMPARE(output->colorCurves().green, QVector<quint16>());
|
|
||||||
QCOMPARE(output->colorCurves().blue, QVector<quint16>());
|
|
||||||
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<quint32>(), m_omSpy->first().last().value<quint32>()));
|
|
||||||
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<quint16>(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<quint16>(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"
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "wayland/clientconnection.h"
|
#include "wayland/clientconnection.h"
|
||||||
#include "wayland/display.h"
|
#include "wayland/display.h"
|
||||||
#include "wayland/output_interface.h"
|
#include "wayland/output_interface.h"
|
||||||
#include "wayland/outputmanagement_v2_interface.h"
|
|
||||||
// Wayland
|
// Wayland
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
// system
|
// system
|
||||||
|
@ -28,7 +27,6 @@ private Q_SLOTS:
|
||||||
void testAddRemoveOutput();
|
void testAddRemoveOutput();
|
||||||
void testClientConnection();
|
void testClientConnection();
|
||||||
void testConnectNoSocket();
|
void testConnectNoSocket();
|
||||||
void testOutputManagement();
|
|
||||||
void testAutoSocketName();
|
void testAutoSocketName();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -178,14 +176,6 @@ void TestWaylandServerDisplay::testConnectNoSocket()
|
||||||
close(sv[1]);
|
close(sv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestWaylandServerDisplay::testOutputManagement()
|
|
||||||
{
|
|
||||||
KWaylandServer::Display display;
|
|
||||||
display.addSocketName("kwayland-test-0");
|
|
||||||
display.start();
|
|
||||||
new OutputManagementV2Interface(&display, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestWaylandServerDisplay::testAutoSocketName()
|
void TestWaylandServerDisplay::testAutoSocketName()
|
||||||
{
|
{
|
||||||
QTemporaryDir runtimeDir;
|
QTemporaryDir runtimeDir;
|
||||||
|
|
|
@ -106,7 +106,7 @@ public:
|
||||||
OutputDeviceV2Interface::RgbRange rgbRange() const;
|
OutputDeviceV2Interface::RgbRange rgbRange() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class OutputConfigurationV2InterfacePrivate;
|
friend class OutputConfigurationV2Interface;
|
||||||
explicit OutputChangeSetV2(OutputDeviceV2Interface *outputdevice, QObject *parent = nullptr);
|
explicit OutputChangeSetV2(OutputDeviceV2Interface *outputdevice, QObject *parent = nullptr);
|
||||||
|
|
||||||
std::unique_ptr<OutputChangeSetV2Private> d;
|
std::unique_ptr<OutputChangeSetV2Private> d;
|
||||||
|
|
|
@ -11,49 +11,30 @@
|
||||||
#include "outputdevice_v2_interface.h"
|
#include "outputdevice_v2_interface.h"
|
||||||
#include "utils/common.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-device-v2.h"
|
||||||
#include "qwayland-server-kde-output-management-v2.h"
|
|
||||||
|
|
||||||
#include <wayland-client-protocol.h>
|
using namespace KWin;
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
namespace KWaylandServer
|
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();
|
OutputConfigurationV2Interface::~OutputConfigurationV2Interface()
|
||||||
void sendFailed();
|
{
|
||||||
void emitConfigurationChangeRequested() const;
|
qDeleteAll(changes.begin(), changes.end());
|
||||||
void clearPendingChanges();
|
}
|
||||||
|
|
||||||
bool hasPendingChanges(OutputDeviceV2Interface *outputdevice) const;
|
void OutputConfigurationV2Interface::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable)
|
||||||
OutputChangeSetV2 *pendingChanges(OutputDeviceV2Interface *outputdevice);
|
|
||||||
|
|
||||||
OutputManagementV2Interface *outputManagement;
|
|
||||||
QHash<OutputDeviceV2Interface *, OutputChangeSetV2 *> changes;
|
|
||||||
std::optional<OutputDeviceV2Interface *> 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)
|
|
||||||
{
|
{
|
||||||
Q_UNUSED(resource)
|
Q_UNUSED(resource)
|
||||||
|
|
||||||
|
@ -61,7 +42,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_enable(R
|
||||||
pendingChanges(output)->d->enabled = enable == 1;
|
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)
|
Q_UNUSED(resource)
|
||||||
OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice);
|
OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice);
|
||||||
|
@ -74,7 +55,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_mode(Res
|
||||||
pendingChanges(output)->d->refreshRate = mode->refreshRate();
|
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)
|
Q_UNUSED(resource)
|
||||||
auto toTransform = [transform]() {
|
auto toTransform = [transform]() {
|
||||||
|
@ -103,7 +84,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_transfor
|
||||||
pendingChanges(output)->d->transform = _transform;
|
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)
|
Q_UNUSED(resource)
|
||||||
auto _pos = QPoint(x, y);
|
auto _pos = QPoint(x, y);
|
||||||
|
@ -111,7 +92,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_position
|
||||||
pendingChanges(output)->d->position = _pos;
|
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)
|
Q_UNUSED(resource)
|
||||||
const qreal doubleScale = wl_fixed_to_double(scale);
|
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;
|
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)
|
if (applied) {
|
||||||
emitConfigurationChangeRequested();
|
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<Output::Transform>(changeset->transform());
|
||||||
|
props->overscan = changeset->overscan();
|
||||||
|
props->rgbRange = static_cast<Output::RgbRange>(changeset->rgbRange());
|
||||||
|
props->vrrPolicy = static_cast<RenderLoop::VrrPolicy>(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)
|
Q_UNUSED(resource)
|
||||||
if (overscan > 100) {
|
if (overscan > 100) {
|
||||||
|
@ -142,7 +192,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_overscan
|
||||||
pendingChanges(output)->d->overscan = 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)
|
Q_UNUSED(resource)
|
||||||
if (policy > static_cast<uint32_t>(OutputDeviceV2Interface::VrrPolicy::Automatic)) {
|
if (policy > static_cast<uint32_t>(OutputDeviceV2Interface::VrrPolicy::Automatic)) {
|
||||||
|
@ -153,7 +203,7 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_vrr_
|
||||||
pendingChanges(output)->d->vrrPolicy = static_cast<OutputDeviceV2Interface::VrrPolicy>(policy);
|
pendingChanges(output)->d->vrrPolicy = static_cast<OutputDeviceV2Interface::VrrPolicy>(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)
|
Q_UNUSED(resource)
|
||||||
if (rgbRange > static_cast<uint32_t>(OutputDeviceV2Interface::RgbRange::Limited)) {
|
if (rgbRange > static_cast<uint32_t>(OutputDeviceV2Interface::RgbRange::Limited)) {
|
||||||
|
@ -164,107 +214,30 @@ void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_rgb_
|
||||||
pendingChanges(output)->d->rgbRange = static_cast<OutputDeviceV2Interface::RgbRange>(rgbRange);
|
pendingChanges(output)->d->rgbRange = static_cast<OutputDeviceV2Interface::RgbRange>(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);
|
Q_UNUSED(resource);
|
||||||
primaryOutput = OutputDeviceV2Interface::get(output);
|
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);
|
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)
|
Q_UNUSED(resource)
|
||||||
delete q;
|
delete this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputConfigurationV2InterfacePrivate::emitConfigurationChangeRequested() const
|
OutputChangeSetV2 *OutputConfigurationV2Interface::pendingChanges(OutputDeviceV2Interface *outputdevice)
|
||||||
{
|
|
||||||
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<OutputDeviceV2Interface *, OutputChangeSetV2 *> 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)
|
|
||||||
{
|
{
|
||||||
auto &change = changes[outputdevice];
|
auto &change = changes[outputdevice];
|
||||||
if (!change) {
|
if (!change) {
|
||||||
change = new OutputChangeSetV2(outputdevice, q);
|
change = new OutputChangeSetV2(outputdevice);
|
||||||
}
|
}
|
||||||
return change;
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,88 +6,44 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "kwin_export.h"
|
#include "qwayland-server-kde-output-management-v2.h"
|
||||||
|
|
||||||
#include "outputchangeset_v2.h"
|
#include "outputchangeset_v2.h"
|
||||||
#include "outputdevice_v2_interface.h"
|
#include "outputdevice_v2_interface.h"
|
||||||
#include "outputmanagement_v2_interface.h"
|
#include "outputmanagement_v2_interface.h"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace KWaylandServer
|
namespace KWaylandServer
|
||||||
{
|
{
|
||||||
|
|
||||||
class OutputConfigurationV2InterfacePrivate;
|
class OutputConfigurationV2Interface : public QtWaylandServer::kde_output_configuration_v2
|
||||||
|
|
||||||
/** @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
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
|
explicit OutputConfigurationV2Interface(wl_resource *resource);
|
||||||
~OutputConfigurationV2Interface() override;
|
~OutputConfigurationV2Interface() override;
|
||||||
|
|
||||||
/**
|
OutputChangeSetV2 *pendingChanges(OutputDeviceV2Interface *outputdevice);
|
||||||
* 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<OutputDeviceV2Interface *, OutputChangeSetV2 *> changes() const;
|
|
||||||
|
|
||||||
bool primaryChanged() const;
|
bool applied = false;
|
||||||
OutputDeviceV2Interface *primary() const;
|
QHash<OutputDeviceV2Interface *, OutputChangeSetV2 *> changes;
|
||||||
|
std::optional<OutputDeviceV2Interface *> primaryOutput;
|
||||||
|
|
||||||
public Q_SLOTS:
|
protected:
|
||||||
/**
|
void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override;
|
||||||
* Called by the compositor once the changes have successfully been applied.
|
void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override;
|
||||||
* The compositor is responsible for updating the OutputDevices. After having
|
void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override;
|
||||||
* done so, calling this function sends applied() through the client.
|
void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override;
|
||||||
* @see setFailed
|
void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override;
|
||||||
* @see OutputConfiguration::applied
|
void kde_output_configuration_v2_apply(Resource *resource) override;
|
||||||
*/
|
void kde_output_configuration_v2_destroy(Resource *resource) override;
|
||||||
void setApplied();
|
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;
|
||||||
* Called by the compositor when the changes as a whole are rejected or
|
void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override;
|
||||||
* failed to apply. This function results in the client OutputConfiguration emitting
|
void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override;
|
||||||
* failed().
|
void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override;
|
||||||
* @see setApplied
|
|
||||||
* @see OutputConfiguration::failed
|
|
||||||
*/
|
|
||||||
void setFailed();
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit OutputConfigurationV2Interface(OutputManagementV2Interface *parent, wl_resource *resource);
|
|
||||||
friend class OutputManagementV2InterfacePrivate;
|
|
||||||
|
|
||||||
std::unique_ptr<OutputConfigurationV2InterfacePrivate> d;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(KWaylandServer::OutputConfigurationV2Interface *)
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ void OutputManagementV2InterfacePrivate::kde_output_management_v2_create_configu
|
||||||
wl_client_post_no_memory(resource->client());
|
wl_client_post_no_memory(resource->client());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
new OutputConfigurationV2Interface(q, config_resource);
|
new OutputConfigurationV2Interface(config_resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputManagementV2Interface::OutputManagementV2Interface(Display *display, QObject *parent)
|
OutputManagementV2Interface::OutputManagementV2Interface(Display *display, QObject *parent)
|
||||||
|
|
|
@ -15,7 +15,6 @@ namespace KWaylandServer
|
||||||
|
|
||||||
class Display;
|
class Display;
|
||||||
class OutputManagementV2InterfacePrivate;
|
class OutputManagementV2InterfacePrivate;
|
||||||
class OutputConfigurationV2Interface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class OutputManagementInterface
|
* @class OutputManagementInterface
|
||||||
|
@ -38,22 +37,6 @@ public:
|
||||||
explicit OutputManagementV2Interface(Display *display, QObject *parent = nullptr);
|
explicit OutputManagementV2Interface(Display *display, QObject *parent = nullptr);
|
||||||
~OutputManagementV2Interface() override;
|
~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:
|
private:
|
||||||
std::unique_ptr<OutputManagementV2InterfacePrivate> d;
|
std::unique_ptr<OutputManagementV2InterfacePrivate> d;
|
||||||
};
|
};
|
||||||
|
|
|
@ -465,9 +465,6 @@ bool WaylandServer::init(InitializationFlags flags)
|
||||||
});
|
});
|
||||||
|
|
||||||
m_outputManagement = new OutputManagementV2Interface(m_display, m_display);
|
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_primary = new PrimaryOutputV1Interface(m_display, m_display);
|
||||||
|
|
||||||
m_xdgOutputManagerV1 = new XdgOutputManagerV1Interface(m_display, m_display);
|
m_xdgOutputManagerV1 = new XdgOutputManagerV1Interface(m_display, m_display);
|
||||||
|
|
Loading…
Reference in a new issue