diff --git a/autotests/client/test_wayland_outputdevice.cpp b/autotests/client/test_wayland_outputdevice.cpp index 4807713..a21f179 100644 --- a/autotests/client/test_wayland_outputdevice.cpp +++ b/autotests/client/test_wayland_outputdevice.cpp @@ -1,683 +1,695 @@ /******************************************************************** Copyright 2014 Martin Gräßlin Copyright 2015 Sebastian Kügler This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/outputdevice.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/outputdevice_interface.h" // Wayland #include using namespace KWayland::Client; using namespace KWayland::Server; class TestWaylandOutputDevice : public QObject { Q_OBJECT public: explicit TestWaylandOutputDevice(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testRegistry(); void testModeChanges(); void testScaleChange_legacy(); void testScaleChange(); void testColorCurvesChange(); void testSubPixel_data(); void testSubPixel(); void testTransform_data(); void testTransform(); void testEnabled(); void testEdid(); void testId(); void testDone(); private: KWayland::Server::Display *m_display; KWayland::Server::OutputDeviceInterface *m_serverOutputDevice; QByteArray m_edid; + QString m_serialNumber; + QString m_eidaId; + KWayland::Server::OutputDeviceInterface::ColorCurves m_initColorCurves; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-output-0"); TestWaylandOutputDevice::TestWaylandOutputDevice(QObject *parent) : QObject(parent) , m_display(nullptr) , m_serverOutputDevice(nullptr) , m_connection(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWaylandOutputDevice::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_serverOutputDevice = m_display->createOutputDevice(this); m_serverOutputDevice->setUuid("1337"); OutputDeviceInterface::Mode m0; m0.id = 0; m0.size = QSize(800, 600); m0.flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); m_serverOutputDevice->addMode(m0); OutputDeviceInterface::Mode m1; m1.id = 1; m1.size = QSize(1024, 768); m_serverOutputDevice->addMode(m1); OutputDeviceInterface::Mode m2; m2.id = 2; m2.size = QSize(1280, 1024); m2.refreshRate = 90000; m_serverOutputDevice->addMode(m2); m_serverOutputDevice->setCurrentMode(1); m_edid = QByteArray::fromBase64("AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg=="); m_serverOutputDevice->setEdid(m_edid); + m_serialNumber = "23498723948723"; + m_serverOutputDevice->setSerialNumber(m_serialNumber); + m_eidaId = "asdffoo"; + m_serverOutputDevice->setEisaId(m_eidaId); + m_initColorCurves.red.clear(); m_initColorCurves.green.clear(); m_initColorCurves.blue.clear(); // 8 bit color ramps for (int i = 0; i < 256; i++) { quint16 val = (double)i / 255 * UINT16_MAX; m_initColorCurves.red << val ; m_initColorCurves.green << val ; } // 10 bit color ramp for (int i = 0; i < 320; i++) { m_initColorCurves.blue << (double)i / 319 * UINT16_MAX; } m_serverOutputDevice->setColorCurves(m_initColorCurves); m_serverOutputDevice->create(); // 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()); } void TestWaylandOutputDevice::cleanup() { if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_serverOutputDevice; m_serverOutputDevice = nullptr; delete m_display; m_display = nullptr; } void TestWaylandOutputDevice::testRegistry() { m_serverOutputDevice->setGlobalPosition(QPoint(100, 50)); m_serverOutputDevice->setPhysicalSize(QSize(200, 100)); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QVERIFY(!output.isValid()); QCOMPARE(output.uuid(), QByteArray()); 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.scale(), 1); 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.eisaId(), QString()); + QCOMPARE(output.serialNumber(), QString()); + QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.geometry(), QRect(100, 50, 1024, 768)); QCOMPARE(output.globalPosition(), QPoint(100, 50)); QCOMPARE(output.manufacturer(), QStringLiteral("org.kde.kwin")); QCOMPARE(output.model(), QStringLiteral("none")); QCOMPARE(output.physicalSize(), QSize(200, 100)); QCOMPARE(output.pixelSize(), QSize(1024, 768)); QCOMPARE(output.refreshRate(), 60000); QCOMPARE(output.scale(), 1); QCOMPARE(output.colorCurves().red, m_initColorCurves.red); QCOMPARE(output.colorCurves().green, m_initColorCurves.green); QCOMPARE(output.colorCurves().blue, m_initColorCurves.blue); // for xwayland output it's unknown QCOMPARE(output.subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown); // for xwayland transform is normal QCOMPARE(output.transform(), KWayland::Client::OutputDevice::Transform::Normal); QCOMPARE(output.edid(), m_edid); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); QCOMPARE(output.uuid(), QByteArray("1337")); - + QCOMPARE(output.serialNumber(), m_serialNumber); + QCOMPARE(output.eisaId(), m_eidaId); } void TestWaylandOutputDevice::testModeChanges() { using namespace KWayland::Client; KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::changed); QVERIFY(outputChanged.isValid()); QSignalSpy modeAddedSpy(&output, &KWayland::Client::OutputDevice::modeAdded); QVERIFY(modeAddedSpy.isValid()); QSignalSpy doneSpy(&output, &KWayland::Client::OutputDevice::done); QVERIFY(doneSpy.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(doneSpy.wait()); QCOMPARE(modeAddedSpy.count(), 3); QCOMPARE(modeAddedSpy.at(0).first().value().size, QSize(800, 600)); QCOMPARE(modeAddedSpy.at(0).first().value().refreshRate, 60000); QCOMPARE(modeAddedSpy.at(0).first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Preferred)); QCOMPARE(modeAddedSpy.at(0).first().value().output, QPointer(&output)); QVERIFY(modeAddedSpy.at(0).first().value().id > -1); QCOMPARE(modeAddedSpy.at(1).first().value().size, QSize(1280, 1024)); QCOMPARE(modeAddedSpy.at(1).first().value().refreshRate, 90000); QCOMPARE(modeAddedSpy.at(1).first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::None)); QCOMPARE(modeAddedSpy.at(1).first().value().output, QPointer(&output)); QVERIFY(modeAddedSpy.at(1).first().value().id > -1); QCOMPARE(modeAddedSpy.at(2).first().value().size, QSize(1024, 768)); QCOMPARE(modeAddedSpy.at(2).first().value().refreshRate, 60000); QCOMPARE(modeAddedSpy.at(2).first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Current)); QCOMPARE(modeAddedSpy.at(2).first().value().output, QPointer(&output)); const QList &modes = output.modes(); QVERIFY(modeAddedSpy.at(2).first().value().id > -1); QCOMPARE(modes.size(), 3); QCOMPARE(modes.at(0), modeAddedSpy.at(0).first().value()); QCOMPARE(modes.at(1), modeAddedSpy.at(1).first().value()); QCOMPARE(modes.at(2), modeAddedSpy.at(2).first().value()); QCOMPARE(output.pixelSize(), QSize(1024, 768)); // change the current mode outputChanged.clear(); QSignalSpy modeChangedSpy(&output, &KWayland::Client::OutputDevice::modeChanged); QVERIFY(modeChangedSpy.isValid()); m_serverOutputDevice->setCurrentMode(0); QVERIFY(doneSpy.wait()); QCOMPARE(modeChangedSpy.size(), 2); // the one which lost the current flag QCOMPARE(modeChangedSpy.first().first().value().size, QSize(1024, 768)); QCOMPARE(modeChangedSpy.first().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.first().first().value().flags, OutputDevice::Mode::Flags()); // the one which got the current flag QCOMPARE(modeChangedSpy.last().first().value().size, QSize(800, 600)); QCOMPARE(modeChangedSpy.last().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.last().first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Current | OutputDevice::Mode::Flag::Preferred)); QVERIFY(!outputChanged.isEmpty()); QCOMPARE(output.pixelSize(), QSize(800, 600)); const QList &modes2 = output.modes(); QCOMPARE(modes2.at(0).size, QSize(1280, 1024)); QCOMPARE(modes2.at(0).refreshRate, 90000); QCOMPARE(modes2.at(0).flags, OutputDevice::Mode::Flag::None); QCOMPARE(modes2.at(1).size, QSize(1024, 768)); QCOMPARE(modes2.at(1).refreshRate, 60000); QCOMPARE(modes2.at(1).flags, OutputDevice::Mode::Flag::None); QCOMPARE(modes2.at(2).size, QSize(800, 600)); QCOMPARE(modes2.at(2).refreshRate, 60000); QCOMPARE(modes2.at(2).flags, OutputDevice::Mode::Flag::Current | OutputDevice::Mode::Flag::Preferred); // change once more outputChanged.clear(); modeChangedSpy.clear(); m_serverOutputDevice->setCurrentMode(2); QVERIFY(doneSpy.wait()); QCOMPARE(modeChangedSpy.size(), 2); // the one which lost the current flag QCOMPARE(modeChangedSpy.first().first().value().size, QSize(800, 600)); QCOMPARE(modeChangedSpy.first().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.first().first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Preferred)); // the one which got the current flag QCOMPARE(modeChangedSpy.last().first().value().size, QSize(1280, 1024)); QCOMPARE(modeChangedSpy.last().first().value().refreshRate, 90000); QCOMPARE(modeChangedSpy.last().first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Current)); QVERIFY(!outputChanged.isEmpty()); QCOMPARE(output.pixelSize(), QSize(1280, 1024)); } void TestWaylandOutputDevice::testScaleChange_legacy() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 1); // change the scale outputChanged.clear(); m_serverOutputDevice->setScale(2); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 2); QCOMPARE(output.scaleF(), 2.0); //check we're forward compatiable // change once more outputChanged.clear(); m_serverOutputDevice->setScale(4); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 4); QCOMPARE(output.scaleF(), 4.0); } void TestWaylandOutputDevice::testScaleChange() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.scaleF(), 1.0); // change the scale outputChanged.clear(); m_serverOutputDevice->setScaleF(2.2); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 2); //check backwards compatibility works QCOMPARE(wl_fixed_from_double(output.scaleF()), wl_fixed_from_double(2.2)); // change once more outputChanged.clear(); m_serverOutputDevice->setScaleF(4.9); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 5); QCOMPARE(wl_fixed_from_double(output.scaleF()), wl_fixed_from_double(4.9)); } void TestWaylandOutputDevice::testColorCurvesChange() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.colorCurves().red, m_initColorCurves.red); QCOMPARE(output.colorCurves().green, m_initColorCurves.green); QCOMPARE(output.colorCurves().blue, m_initColorCurves.blue); // change the color curves outputChanged.clear(); KWayland::Server::OutputDeviceInterface::ColorCurves cc; cc.red = QVector(256, 0); cc.green = QVector(256, UINT16_MAX); cc.blue = QVector(320, 1); m_serverOutputDevice->setColorCurves(cc); QVERIFY(outputChanged.wait()); QCOMPARE(output.colorCurves().red, cc.red); QCOMPARE(output.colorCurves().green, cc.green); QCOMPARE(output.colorCurves().blue, cc.blue); // change once more outputChanged.clear(); cc.red = QVector(256, 0); cc.green = QVector(256, UINT16_MAX); cc.blue = QVector(320, UINT16_MAX); m_serverOutputDevice->setColorCurves(cc); QVERIFY(outputChanged.wait()); QCOMPARE(output.colorCurves().red, cc.red); QCOMPARE(output.colorCurves().green, cc.green); QCOMPARE(output.colorCurves().blue, cc.blue); } void TestWaylandOutputDevice::testSubPixel_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("expected"); QTest::addColumn("actual"); QTest::newRow("none") << OutputDevice::SubPixel::None << OutputDeviceInterface::SubPixel::None; QTest::newRow("horizontal/rgb") << OutputDevice::SubPixel::HorizontalRGB << OutputDeviceInterface::SubPixel::HorizontalRGB; QTest::newRow("horizontal/bgr") << OutputDevice::SubPixel::HorizontalBGR << OutputDeviceInterface::SubPixel::HorizontalBGR; QTest::newRow("vertical/rgb") << OutputDevice::SubPixel::VerticalRGB << OutputDeviceInterface::SubPixel::VerticalRGB; QTest::newRow("vertical/bgr") << OutputDevice::SubPixel::VerticalBGR << OutputDeviceInterface::SubPixel::VerticalBGR; } void TestWaylandOutputDevice::testSubPixel() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(OutputDeviceInterface::SubPixel, actual); m_serverOutputDevice->setSubPixel(actual); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QTEST(output.subPixel(), "expected"); // change back to unknown outputChanged.clear(); m_serverOutputDevice->setSubPixel(OutputDeviceInterface::SubPixel::Unknown); QVERIFY(outputChanged.wait()); QCOMPARE(output.subPixel(), OutputDevice::SubPixel::Unknown); } void TestWaylandOutputDevice::testTransform_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("expected"); QTest::addColumn("actual"); QTest::newRow("90") << OutputDevice::Transform::Rotated90 << OutputDeviceInterface::Transform::Rotated90; QTest::newRow("180") << OutputDevice::Transform::Rotated180 << OutputDeviceInterface::Transform::Rotated180; QTest::newRow("270") << OutputDevice::Transform::Rotated270 << OutputDeviceInterface::Transform::Rotated270; QTest::newRow("Flipped") << OutputDevice::Transform::Flipped << OutputDeviceInterface::Transform::Flipped; QTest::newRow("Flipped 90") << OutputDevice::Transform::Flipped90 << OutputDeviceInterface::Transform::Flipped90; QTest::newRow("Flipped 180") << OutputDevice::Transform::Flipped180 << OutputDeviceInterface::Transform::Flipped180; QTest::newRow("Flipped 280") << OutputDevice::Transform::Flipped270 << OutputDeviceInterface::Transform::Flipped270; } void TestWaylandOutputDevice::testTransform() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(OutputDeviceInterface::Transform, actual); m_serverOutputDevice->setTransform(actual); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice *output = registry.createOutputDevice(announced.first().first().value(), announced.first().last().value(), ®istry); QSignalSpy outputChanged(output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QTEST(output->transform(), "expected"); // change back to normal outputChanged.clear(); m_serverOutputDevice->setTransform(OutputDeviceInterface::Transform::Normal); QVERIFY(outputChanged.wait()); QCOMPARE(output->transform(), OutputDevice::Transform::Normal); } void TestWaylandOutputDevice::testEnabled() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); QSignalSpy changed(&output, &KWayland::Client::OutputDevice::changed); QSignalSpy enabledChanged(&output, &KWayland::Client::OutputDevice::enabledChanged); QVERIFY(enabledChanged.isValid()); m_serverOutputDevice->setEnabled(OutputDeviceInterface::Enablement::Disabled); QVERIFY(enabledChanged.wait()); QCOMPARE(output.enabled(), OutputDevice::Enablement::Disabled); if (changed.count() != enabledChanged.count()) { QVERIFY(changed.wait()); } QCOMPARE(changed.count(), enabledChanged.count()); m_serverOutputDevice->setEnabled(OutputDeviceInterface::Enablement::Enabled); QVERIFY(enabledChanged.wait()); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); if (changed.count() != enabledChanged.count()) { QVERIFY(changed.wait()); } QCOMPARE(changed.count(), enabledChanged.count()); } void TestWaylandOutputDevice::testEdid() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QCOMPARE(output.edid(), QByteArray()); QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.edid(), m_edid); } void TestWaylandOutputDevice::testId() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.uuid(), QByteArray("1337")); QSignalSpy idChanged(&output, &KWayland::Client::OutputDevice::uuidChanged); QVERIFY(idChanged.isValid()); m_serverOutputDevice->setUuid("42"); QVERIFY(idChanged.wait()); QCOMPARE(idChanged.first().first().toByteArray(), QByteArray("42")); idChanged.clear(); QCOMPARE(output.uuid(), QByteArray("42")); m_serverOutputDevice->setUuid("4711"); QVERIFY(idChanged.wait()); QCOMPARE(idChanged.first().first().toByteArray(), QByteArray("4711")); idChanged.clear(); QCOMPARE(output.uuid(), QByteArray("4711")); } void TestWaylandOutputDevice::testDone() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputDone(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputDone.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputDone.wait()); } QTEST_GUILESS_MAIN(TestWaylandOutputDevice) #include "test_wayland_outputdevice.moc" diff --git a/src/client/outputdevice.cpp b/src/client/outputdevice.cpp index f9a7aee..b8e30c3 100644 --- a/src/client/outputdevice.cpp +++ b/src/client/outputdevice.cpp @@ -1,512 +1,555 @@ /******************************************************************** Copyright 2013 Martin Gräßlin Copyright 2018 Roman Gilg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "logging_p.h" #include "outputdevice.h" #include "wayland_pointer_p.h" // Qt #include #include #include #include // wayland #include "wayland-org_kde_kwin_outputdevice-client-protocol.h" #include namespace KWayland { namespace Client { typedef QList Modes; class Q_DECL_HIDDEN OutputDevice::Private { public: Private(OutputDevice *q); void setup(org_kde_kwin_outputdevice *o); WaylandPointer output; EventQueue *queue = nullptr; QSize physicalSize; QPoint globalPosition; QString manufacturer; QString model; qreal scale = 1.0; + QString serialNumber; + QString eisaId; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; Modes modes; Modes::iterator currentMode = modes.end(); QByteArray edid; OutputDevice::Enablement enabled = OutputDevice::Enablement::Enabled; QByteArray uuid; ColorCurves colorCurves; bool done = false; private: static void geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform); static void modeCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id); static void doneCallback(void *data, org_kde_kwin_outputdevice *output); static void scaleCallback(void *data, org_kde_kwin_outputdevice *output, int32_t scale); static void scaleFCallback(void *data, org_kde_kwin_outputdevice *output, wl_fixed_t scale); static void edidCallback(void *data, org_kde_kwin_outputdevice *output, const char *raw); static void enabledCallback(void *data, org_kde_kwin_outputdevice *output, int32_t enabled); static void uuidCallback(void *data, org_kde_kwin_outputdevice *output, const char *uuid); static void colorcurvesCallback(void *data, org_kde_kwin_outputdevice *output, wl_array *red, wl_array *green, wl_array *blue); + static void serialNumberCallback(void *data, org_kde_kwin_outputdevice *output, const char *serialNumber); + static void eisaIdCallback(void *data, org_kde_kwin_outputdevice *output, const char *eisa); + void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); void setScale(qreal scale); + void setSerialNumber(const QString &serialNumber); + void setEisaId(const QString &eisaId); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id); OutputDevice *q; static struct org_kde_kwin_outputdevice_listener s_outputListener; }; OutputDevice::Private::Private(OutputDevice *q) : q(q) { } void OutputDevice::Private::setup(org_kde_kwin_outputdevice *o) { Q_ASSERT(o); Q_ASSERT(!output); output.setup(o); org_kde_kwin_outputdevice_add_listener(output, &s_outputListener, this); } bool OutputDevice::Mode::operator==(const OutputDevice::Mode &m) const { return size == m.size && refreshRate == m.refreshRate && flags == m.flags && output == m.output; } bool OutputDevice::ColorCurves::operator==(const OutputDevice::ColorCurves &cc) const { return red == cc.red && green == cc.green && blue == cc.blue; } bool OutputDevice::ColorCurves::operator!=(const ColorCurves &cc) const { return !operator==(cc); } OutputDevice::OutputDevice(QObject *parent) : QObject(parent) , d(new Private(this)) { } OutputDevice::~OutputDevice() { d->output.release(); } org_kde_kwin_outputdevice_listener OutputDevice::Private::s_outputListener = { geometryCallback, modeCallback, doneCallback, scaleCallback, edidCallback, enabledCallback, uuidCallback, scaleFCallback, - colorcurvesCallback + colorcurvesCallback, + serialNumberCallback, + eisaIdCallback }; void OutputDevice::Private::geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform) { Q_UNUSED(transform) auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setGlobalPosition(QPoint(x, y)); o->setManufacturer(make); o->setModel(model); o->setPhysicalSize(QSize(physicalWidth, physicalHeight)); auto toSubPixel = [subPixel]() { switch (subPixel) { case WL_OUTPUT_SUBPIXEL_NONE: return SubPixel::None; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: return SubPixel::HorizontalRGB; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: return SubPixel::HorizontalBGR; case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: return SubPixel::VerticalRGB; case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: return SubPixel::VerticalBGR; case WL_OUTPUT_SUBPIXEL_UNKNOWN: default: return SubPixel::Unknown; } }; o->setSubPixel(toSubPixel()); auto toTransform = [transform]() { switch (transform) { case WL_OUTPUT_TRANSFORM_90: return Transform::Rotated90; case WL_OUTPUT_TRANSFORM_180: return Transform::Rotated180; case WL_OUTPUT_TRANSFORM_270: return Transform::Rotated270; case WL_OUTPUT_TRANSFORM_FLIPPED: return Transform::Flipped; case WL_OUTPUT_TRANSFORM_FLIPPED_90: return Transform::Flipped90; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return Transform::Flipped180; case WL_OUTPUT_TRANSFORM_FLIPPED_270: return Transform::Flipped270; case WL_OUTPUT_TRANSFORM_NORMAL: default: return Transform::Normal; } }; o->setTransform(toTransform()); } void OutputDevice::Private::modeCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->addMode(flags, width, height, refresh, mode_id); } void OutputDevice::Private::addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id) { Mode mode; mode.output = QPointer(q); mode.refreshRate = refresh; mode.size = QSize(width, height); mode.id = mode_id; if (flags & WL_OUTPUT_MODE_CURRENT) { mode.flags |= Mode::Flag::Current; } if (flags & WL_OUTPUT_MODE_PREFERRED) { mode.flags |= Mode::Flag::Preferred; } auto currentIt = modes.insert(modes.end(), mode); bool existing = false; if (flags & WL_OUTPUT_MODE_CURRENT) { auto it = modes.begin(); while (it != currentIt) { auto &m = (*it); if (m.flags.testFlag(Mode::Flag::Current)) { m.flags &= ~Mode::Flags(Mode::Flag::Current); emit q->modeChanged(m); } if (m.refreshRate == mode.refreshRate && m.size == mode.size) { it = modes.erase(it); existing = true; } else { it++; } } currentMode = currentIt; } if (existing) { emit q->modeChanged(mode); } else { emit q->modeAdded(mode); } } KWayland::Client::OutputDevice::Mode OutputDevice::currentMode() const { for (const auto &m: modes()) { if (m.flags.testFlag(KWayland::Client::OutputDevice::Mode::Flag::Current)) { return m; } } qCWarning(KWAYLAND_CLIENT) << "current mode not found"; return Mode(); } void OutputDevice::Private::scaleCallback(void *data, org_kde_kwin_outputdevice *output, int32_t scale) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setScale(scale); } void OutputDevice::Private::scaleFCallback(void *data, org_kde_kwin_outputdevice *output, wl_fixed_t scale_fixed) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setScale(wl_fixed_to_double(scale_fixed)); } void OutputDevice::Private::doneCallback(void *data, org_kde_kwin_outputdevice *output) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->done = true; emit o->q->changed(); emit o->q->done(); } void OutputDevice::Private::edidCallback(void* data, org_kde_kwin_outputdevice* output, const char* raw) { Q_UNUSED(output); auto o = reinterpret_cast(data); o->edid = QByteArray::fromBase64(raw); } void OutputDevice::Private::enabledCallback(void* data, org_kde_kwin_outputdevice* output, int32_t enabled) { Q_UNUSED(output); auto o = reinterpret_cast(data); OutputDevice::Enablement _enabled = OutputDevice::Enablement::Disabled; if (enabled == ORG_KDE_KWIN_OUTPUTDEVICE_ENABLEMENT_ENABLED) { _enabled = OutputDevice::Enablement::Enabled; } if (o->enabled != _enabled) { o->enabled = _enabled; emit o->q->enabledChanged(o->enabled); if (o->done) { emit o->q->changed(); } } } void OutputDevice::Private::uuidCallback(void* data, org_kde_kwin_outputdevice* output, const char *uuid) { Q_UNUSED(output); auto o = reinterpret_cast(data); if (o->uuid != uuid) { o->uuid = uuid; emit o->q->uuidChanged(o->uuid); if (o->done) { emit o->q->changed(); } } } void OutputDevice::Private::colorcurvesCallback(void *data, org_kde_kwin_outputdevice *output, wl_array *red, wl_array *green, wl_array *blue) { Q_UNUSED(output); auto o = reinterpret_cast(data); auto cc = ColorCurves(); auto setCurve = [](const wl_array *curve, QVector *destination) { destination->resize(curve->size / sizeof(uint16_t)); memcpy(destination->data(), curve->data, curve->size); }; setCurve(red, &cc.red); setCurve(green, &cc.green); setCurve(blue, &cc.blue); if (o->colorCurves != cc) { o->colorCurves = cc; emit o->q->colorCurvesChanged(); if (o->done) { emit o->q->changed(); } } } +void OutputDevice::Private::serialNumberCallback(void *data, org_kde_kwin_outputdevice *output, const char *raw) +{ + auto o = reinterpret_cast(data); + Q_UNUSED(output); + o->setSerialNumber(raw); +} + +void OutputDevice::Private::eisaIdCallback(void *data, org_kde_kwin_outputdevice *output, const char *raw) +{ + auto o = reinterpret_cast(data); + Q_UNUSED(output); + o->setEisaId(raw); +} + void OutputDevice::setup(org_kde_kwin_outputdevice *output) { d->setup(output); } EventQueue *OutputDevice::eventQueue() const { return d->queue; } void OutputDevice::setEventQueue(EventQueue *queue) { d->queue = queue; } void OutputDevice::Private::setGlobalPosition(const QPoint &pos) { globalPosition = pos; } void OutputDevice::Private::setManufacturer(const QString &m) { manufacturer = m; } void OutputDevice::Private::setModel(const QString &m) { model = m; } +void OutputDevice::Private::setSerialNumber(const QString &sn) +{ + serialNumber = sn; +} + +void OutputDevice::Private::setEisaId(const QString &e) +{ + eisaId = e; +} + void OutputDevice::Private::setPhysicalSize(const QSize &size) { physicalSize = size; } void OutputDevice::Private::setScale(qreal s) { scale = s; } QRect OutputDevice::geometry() const { if (d->currentMode == d->modes.end()) { return QRect(); } return QRect(d->globalPosition, pixelSize()); } void OutputDevice::Private::setSubPixel(OutputDevice::SubPixel s) { subPixel = s; } void OutputDevice::Private::setTransform(OutputDevice::Transform t) { transform = t; } QPoint OutputDevice::globalPosition() const { return d->globalPosition; } QString OutputDevice::manufacturer() const { return d->manufacturer; } QString OutputDevice::model() const { return d->model; } +QString OutputDevice::serialNumber() const +{ + return d->serialNumber; +} + +QString OutputDevice::eisaId() const +{ + return d->eisaId; +} + org_kde_kwin_outputdevice *OutputDevice::output() { return d->output; } QSize OutputDevice::physicalSize() const { return d->physicalSize; } QSize OutputDevice::pixelSize() const { if (d->currentMode == d->modes.end()) { return QSize(); } return (*d->currentMode).size; } int OutputDevice::refreshRate() const { if (d->currentMode == d->modes.end()) { return 0; } return (*d->currentMode).refreshRate; } int OutputDevice::scale() const { return qRound(d->scale); } qreal OutputDevice::scaleF() const { return d->scale; } bool OutputDevice::isValid() const { return d->output.isValid(); } OutputDevice::SubPixel OutputDevice::subPixel() const { return d->subPixel; } OutputDevice::Transform OutputDevice::transform() const { return d->transform; } QList< OutputDevice::Mode > OutputDevice::modes() const { return d->modes; } OutputDevice::operator org_kde_kwin_outputdevice*() { return d->output; } OutputDevice::operator org_kde_kwin_outputdevice*() const { return d->output; } QByteArray OutputDevice::edid() const { return d->edid; } OutputDevice::Enablement OutputDevice::enabled() const { return d->enabled; } QByteArray OutputDevice::uuid() const { return d->uuid; } OutputDevice::ColorCurves OutputDevice::colorCurves() const { return d->colorCurves; } void OutputDevice::destroy() { d->output.destroy(); } } } diff --git a/src/client/outputdevice.h b/src/client/outputdevice.h index 6589ff8..58f11a8 100644 --- a/src/client/outputdevice.h +++ b/src/client/outputdevice.h @@ -1,328 +1,336 @@ /******************************************************************** Copyright 2013 Martin Gräßlin Copyright 2018 Roman Gilg This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_OUTPUTDEVICE_H #define WAYLAND_OUTPUTDEVICE_H #include #include #include #include #include struct org_kde_kwin_outputdevice; class QPoint; class QRect; namespace KWayland { namespace Client { class EventQueue; /** * @short Wrapper for the org_kde_kwin_outputdevice interface. * * This class provides a convenient wrapper for the org_kde_kwin_outputdevice interface. * Its main purpose is to hold the information about one OutputDevice. * * To use this class one needs to interact with the Registry. There are two * possible ways to create an OutputDevice interface: * @code * OutputDevice *c = registry->createOutputDevice(name, version); * @endcode * * This creates the OutputDevice and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * OutputDevice *c = new OutputDevice; * c->setup(registry->bindOutputDevice(name, version)); * @endcode * * The OutputDevice can be used as a drop-in replacement for any org_kde_kwin_outputdevice * pointer as it provides matching cast operators. * * Please note that all properties of OutputDevice are not valid until the * changed signal has been emitted. The wayland server is pushing the * information in an async way to the OutputDevice instance. By emitting changed * the OutputDevice indicates that all relevant information is available. * * @see Registry * @since 5.5 **/ class KWAYLANDCLIENT_EXPORT OutputDevice : public QObject { Q_OBJECT public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; enum class Enablement { Disabled = 0, Enabled = 1 }; struct Mode { enum class Flag { None = 0, Current = 1 << 0, Preferred = 1 << 1 }; Q_DECLARE_FLAGS(Flags, Flag) /** * The size of this Mode in pixel space. **/ QSize size; /** * The refresh rate in mHz of this Mode. **/ int refreshRate = 0; /** * The flags of this Mode, that is whether it's the * Current and/or Preferred Mode of the OutputDevice. **/ Flags flags = Flag::None; /** * The OutputDevice to which this Mode belongs. **/ QPointer output; /** * The id of this mode, unique per OutputDevice. This id can be used to call * OutputConfiguration->setMode(); * @see OutputConfiguration::setMode **/ int id; bool operator==(const Mode &m) const; }; struct ColorCurves { QVector red, green, blue; bool operator==(const ColorCurves &cc) const; bool operator!=(const ColorCurves &cc) const; }; explicit OutputDevice(QObject *parent = nullptr); virtual ~OutputDevice(); /** * Setup this Compositor to manage the @p output. * When using Registry::createOutputDevice there is no need to call this * method. **/ void setup(org_kde_kwin_outputdevice *output); /** * @returns @c true if managing a org_kde_kwin_outputdevice. **/ bool isValid() const; operator org_kde_kwin_outputdevice*(); operator org_kde_kwin_outputdevice*() const; org_kde_kwin_outputdevice *output(); /** * Size in millimeters. **/ QSize physicalSize() const; /** * Position within the global compositor space. **/ QPoint globalPosition() const; /** * Textual description of the manufacturer. **/ QString manufacturer() const; /** * Textual description of the model. **/ QString model() const; + /** + * Textual representation of serial number. + */ + QString serialNumber() const; + /** + * Textual representation of EISA identifier. + */ + QString eisaId() const; /** * Size in the current mode. **/ QSize pixelSize() const; /** * The geometry of this OutputDevice in pixels. * Convenient for QRect(globalPosition(), pixelSize()). * @see globalPosition * @see pixelSize **/ QRect geometry() const; /** * Refresh rate in mHz of the current mode. **/ int refreshRate() const; /** * Scaling factor of this output. * * A scale larger than 1 means that the compositor will automatically scale surface buffers * by this amount when rendering. This is used for very high resolution displays where * applications rendering at the native resolution would be too small to be legible. * @deprecated see scaleF **/ int scale() const; /** * Scaling factor of this output. * * A scale larger than 1 means that the compositor will automatically scale surface buffers * by this amount when rendering. This is used for very high resolution displays where * applications rendering at the native resolution would be too small to be legible. * @since 5.50 **/ qreal scaleF() const; /** * Subpixel orientation of this OutputDevice. **/ SubPixel subPixel() const; /** * Transform that maps framebuffer to OutputDevice. * * The purpose is mainly to allow clients render accordingly and tell the compositor, * so that for fullscreen surfaces, the compositor will still be able to scan out * directly from client surfaces. **/ Transform transform() const; /** * Color curves. * @since 5.50 **/ ColorCurves colorCurves() const; /** * @returns The Modes of this OutputDevice. **/ QList modes() const; KWayland::Client::OutputDevice::Mode currentMode() const; /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue() const; /** * @returns The EDID information for this output. **/ QByteArray edid() const; /** * @returns Whether this output is enabled or not. **/ OutputDevice::Enablement enabled() const; /** * @returns A unique identifier for this outputdevice, determined by the server. **/ QByteArray uuid() const; /** * Destroys the data hold by this OutputDevice. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid any more, it's not * possible to call release any more as that calls into the Wayland * connection and the call would fail. * * This method is automatically invoked when the Registry which created this * Output gets destroyed. **/ void destroy(); Q_SIGNALS: /** * Emitted when the output is fully initialized. **/ void done(); /** * Emitted whenever at least one of the data changed. **/ void changed(); /** * Emitted whenever the enabled property changes. **/ void enabledChanged(OutputDevice::Enablement enabled); /** * Emitted whenever the id property changes. **/ void uuidChanged(const QByteArray &uuid); /** * Emitted whenever a new Mode is added. * This normally only happens during the initial promoting of modes. * Afterwards only modeChanged should be emitted. * @param mode The newly added Mode. * @see modeChanged **/ void modeAdded(const KWayland::Client::OutputDevice::Mode &mode); /** * Emitted whenever a Mode changes. * This normally means that the @c Mode::Flag::Current is added or removed. * @param mode The changed Mode **/ void modeChanged(const KWayland::Client::OutputDevice::Mode &mode); /** * Emitted whenever the color curves changed. * * @since 5.TODO **/ void colorCurvesChanged(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the OutputDevice got created by * Registry::createOutputDevice * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::SubPixel) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Transform) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Enablement) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Mode) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::ColorCurves) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::OutputDevice::Mode::Flags) #endif diff --git a/src/client/protocols/outputdevice.xml b/src/client/protocols/outputdevice.xml index 48cc6bd..8a0c1ca 100644 --- a/src/client/protocols/outputdevice.xml +++ b/src/client/protocols/outputdevice.xml @@ -1,280 +1,295 @@ Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ]]> An outputdevice describes a display device available to the compositor. outputdevice is similar to wl_output, but focuses on output configuration management. A client can query all global outputdevice objects to enlist all available display devices, even those that may currently not be represented by the compositor as a wl_output. The client sends configuration changes to the server through the outputconfiguration interface, and the server applies the configuration changes to the hardware and signals changes to the outputdevices accordingly. This object is published as global during start up for every available display devcies, or when one later becomes available, for example by being hotplugged via a physical connector. This enumeration describes how the physical pixels on an output are laid out. This describes the transform, that a compositor will apply to a surface to compensate for the rotation or mirroring of an output device. The flipped values correspond to an initial flip around a vertical axis followed by rotation. The purpose is mainly to allow clients to render accordingly and tell the compositor, so that for fullscreen surfaces, the compositor is still able to scan out directly client surfaces. The geometry event describes geometric properties of the output. The event is sent when binding to the output object and whenever any of the properties change. These flags describe properties of an output mode. They are used in the flags bitfield of the mode event. The mode event describes an available mode for the output. When the client binds to the outputdevice object, the server sends this event once for every available mode the outputdevice can be operated by. There will always be at least one event sent out on initial binding, which represents the current mode. Later on if an output changes its mode the event is sent again, whereby this event represents the mode that has now become current. In other words, the current mode is always represented by the latest event sent with the current flag set. The size of a mode is given in physical hardware units of the output device. This is not necessarily the same as the output size in the global compositor space. For instance, the output may be scaled, as described in org_kde_kwin_outputdevice.scale, or transformed, as described in org_kde_kwin_outputdevice.transform. The id can be used to refer to a mode when calling set_mode on an org_kde_kwin_outputconfiguration object. This event is sent after all other properties have been sent on binding to the output object as well as after any other output property change have been applied later on. This allows to see changes to the output properties as atomic, even if multiple events successively announce them. This event contains scaling geometry information that is not in the geometry event. It may be sent after binding the output object or if the output scale changes later. If it is not sent, the client should assume a scale of 1. A scale larger than 1 means that the compositor will automatically scale surface buffers by this amount when rendering. This is used for high resolution displays where applications rendering at the native resolution would be too small to be legible. It is intended that scaling aware clients track the current output of a surface, and if it is on a scaled output it should use wl_surface.set_buffer_scale with the scale of the output. That way the compositor can avoid scaling the surface, and the client can supply a higher detail image. The edid event encapsulates the EDID data for the outputdevice. The event is sent when binding to the output object. The EDID data may be empty, in which case this event is sent anyway. If the EDID information is empty, you can fall back to the name et al. properties of the outputdevice. Describes whether a device is enabled, i.e. device is used to display content by the compositor. This wraps a boolean around an int to avoid a boolean trap. The enabled event notifies whether this output is currently enabled and used for displaying content by the server. The event is sent when binding to the output object and whenever later on an output changes its state by becoming enabled or disabled. The uuid can be used to identify the output. It's controlled by the server entirely. The server should make sure the uuid is persistent across restarts. An empty uuid is considered invalid. This event contains scaling geometry information that is not in the geometry event. It may be sent after binding the output object or if the output scale changes later. If it is not sent, the client should assume a scale of 1. A scale larger than 1 means that the compositor will automatically scale surface buffers by this amount when rendering. This is used for high resolution displays where applications rendering at the native resolution would be too small to be legible. It is intended that scaling aware clients track the current output of a surface, and if it is on a scaled output it should use wl_surface.set_buffer_scale with the scale of the output. That way the compositor can avoid scaling the surface, and the client can supply a higher detail image. wl_output will keep the output scale as an integer. In every situation except configuring the window manager you want to use that. Decribes the color intensity profile of the output. Commonly used for gamma/color correction. The array contains all color ramp values of the output. For example on 8bit screens there are 256 of them. The array elements are unsigned 16bit integers. + + + Serial ID of the monitor, sent on startup before the first done event. + + + + + + EISA ID of the monitor, sent on startup before the first done event. + + + + diff --git a/src/server/outputdevice_interface.cpp b/src/server/outputdevice_interface.cpp index c272498..77fd006 100644 --- a/src/server/outputdevice_interface.cpp +++ b/src/server/outputdevice_interface.cpp @@ -1,684 +1,723 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "outputdevice_interface.h" #include "global_p.h" #include "display.h" #include "logging_p.h" #include #include "wayland-org_kde_kwin_outputdevice-server-protocol.h" #include namespace KWayland { namespace Server { class OutputDeviceInterface::Private : public Global::Private { public: struct ResourceData { wl_resource *resource; uint32_t version; }; Private(OutputDeviceInterface *q, Display *d); ~Private(); void sendMode(wl_resource *resource, const Mode &mode); void sendDone(const ResourceData &data); void updateGeometry(); void updateScale(); void updateColorCurves(); + void updateSerialNumber(); + void updateEisaId(); void sendUuid(); void sendEdid(); void sendEnabled(); QSize physicalSize; QPoint globalPosition; QString manufacturer = QStringLiteral("org.kde.kwin"); QString model = QStringLiteral("none"); qreal scale = 1.0; + QString serialNumber; + QString eisaId; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; ColorCurves colorCurves; QList modes; QList resources; QByteArray edid; Enablement enabled = Enablement::Enabled; QByteArray uuid; static OutputDeviceInterface *get(wl_resource *native); private: static Private *cast(wl_resource *native); static void unbind(wl_resource *resource); void bind(wl_client *client, uint32_t version, uint32_t id) override; int32_t toTransform() const; int32_t toSubPixel() const; void sendGeometry(wl_resource *resource); void sendScale(const ResourceData &data); void sendColorCurves(const ResourceData &data); + void sendEisaId(const ResourceData &data); + void sendSerialNumber(const ResourceData &data); static const quint32 s_version; OutputDeviceInterface *q; static QVector s_privates; }; const quint32 OutputDeviceInterface::Private::s_version = 2; QVector OutputDeviceInterface::Private::s_privates; OutputDeviceInterface::Private::Private(OutputDeviceInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_outputdevice_interface, s_version) , q(q) { s_privates << this; } OutputDeviceInterface::Private::~Private() { s_privates.removeAll(this); } OutputDeviceInterface *OutputDeviceInterface::Private::get(wl_resource *native) { if (Private *p = cast(native)) { return p->q; } return nullptr; } OutputDeviceInterface::Private *OutputDeviceInterface::Private::cast(wl_resource *native) { for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) { const auto &resources = (*it)->resources; auto rit = std::find_if(resources.begin(), resources.end(), [native] (const ResourceData &data) { return data.resource == native; }); if (rit != resources.end()) { return (*it); } } return nullptr; } OutputDeviceInterface::OutputDeviceInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { Q_D(); connect(this, &OutputDeviceInterface::currentModeChanged, this, [this, d] { auto currentModeIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); }); if (currentModeIt == d->modes.constEnd()) { return; } for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendMode((*it).resource, *currentModeIt); d->sendDone(*it); } wl_display_flush_clients(*(d->display)); } ); connect(this, &OutputDeviceInterface::subPixelChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::transformChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::globalPositionChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::modelChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::manufacturerChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::scaleFChanged, this, [this, d] { d->updateScale(); }); connect(this, &OutputDeviceInterface::scaleChanged, this, [this, d] { d->updateScale(); }); connect(this, &OutputDeviceInterface::colorCurvesChanged, this, [this, d] { d->updateColorCurves(); }); } OutputDeviceInterface::~OutputDeviceInterface() = default; QSize OutputDeviceInterface::pixelSize() const { Q_D(); auto it = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (it == d->modes.end()) { return QSize(); } return (*it).size; } OutputDeviceInterface *OutputDeviceInterface::get(wl_resource* native) { return Private::get(native); } int OutputDeviceInterface::refreshRate() const { Q_D(); auto it = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (it == d->modes.end()) { return 60000; } return (*it).refreshRate; } void OutputDeviceInterface::addMode(Mode &mode) { Q_ASSERT(!isValid()); Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt == d->modes.end() && !mode.flags.testFlag(ModeFlag::Current)) { // no mode with current flag - enforce mode.flags |= ModeFlag::Current; } if (currentModeIt != d->modes.end() && mode.flags.testFlag(ModeFlag::Current)) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } if (mode.flags.testFlag(ModeFlag::Preferred)) { // remove from existing Preferred mode auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Preferred); } ); if (preferredIt != d->modes.end()) { (*preferredIt).flags &= ~uint(ModeFlag::Preferred); } } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [mode](const Mode &mode_it) { return mode.size == mode_it.size && mode.refreshRate == mode_it.refreshRate && mode.id == mode_it.id; } ); auto emitChanges = [this,mode] { emit modesChanged(); if (mode.flags.testFlag(ModeFlag::Current)) { emit refreshRateChanged(mode.refreshRate); emit pixelSizeChanged(mode.size); emit currentModeChanged(); } }; if (existingModeIt != d->modes.end()) { if ((*existingModeIt).flags == mode.flags) { // nothing to do return; } (*existingModeIt).flags = mode.flags; emitChanges(); return; } else { auto idIt = std::find_if(d->modes.begin(), d->modes.end(), [mode](const Mode &mode_it) { return mode.id == mode_it.id; } ); if (idIt != d->modes.end()) { qCWarning(KWAYLAND_SERVER) << "Duplicate Mode id" << mode.id << ": not adding mode" << mode.size << mode.refreshRate; return; } } d->modes << mode; emitChanges(); } void OutputDeviceInterface::setCurrentMode(const int modeId) { Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt != d->modes.end()) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [modeId](const Mode &mode) { return mode.id == modeId; } ); Q_ASSERT(existingModeIt != d->modes.end()); (*existingModeIt).flags |= ModeFlag::Current; emit modesChanged(); emit refreshRateChanged((*existingModeIt).refreshRate); emit pixelSizeChanged((*existingModeIt).size); emit currentModeChanged(); } int32_t OutputDeviceInterface::Private::toTransform() const { switch (transform) { case Transform::Normal: return WL_OUTPUT_TRANSFORM_NORMAL; case Transform::Rotated90: return WL_OUTPUT_TRANSFORM_90; case Transform::Rotated180: return WL_OUTPUT_TRANSFORM_180; case Transform::Rotated270: return WL_OUTPUT_TRANSFORM_270; case Transform::Flipped: return WL_OUTPUT_TRANSFORM_FLIPPED; case Transform::Flipped90: return WL_OUTPUT_TRANSFORM_FLIPPED_90; case Transform::Flipped180: return WL_OUTPUT_TRANSFORM_FLIPPED_180; case Transform::Flipped270: return WL_OUTPUT_TRANSFORM_FLIPPED_270; } abort(); } int32_t OutputDeviceInterface::Private::toSubPixel() const { switch (subPixel) { case SubPixel::Unknown: return WL_OUTPUT_SUBPIXEL_UNKNOWN; case SubPixel::None: return WL_OUTPUT_SUBPIXEL_NONE; case SubPixel::HorizontalRGB: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; case SubPixel::HorizontalBGR: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; case SubPixel::VerticalRGB: return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; case SubPixel::VerticalBGR: return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; } abort(); } void OutputDeviceInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_outputdevice_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_user_data(resource, this); wl_resource_set_destructor(resource, unbind); ResourceData r; r.resource = resource; r.version = version; resources << r; sendGeometry(resource); sendScale(r); sendColorCurves(r); + sendEisaId(r); + sendSerialNumber(r); auto currentModeIt = modes.constEnd(); for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) { const Mode &mode = *it; if (mode.flags.testFlag(ModeFlag::Current)) { // needs to be sent as last mode currentModeIt = it; continue; } sendMode(resource, mode); } if (currentModeIt != modes.constEnd()) { sendMode(resource, *currentModeIt); } sendUuid(); sendEdid(); sendEnabled(); sendDone(r); c->flush(); } void OutputDeviceInterface::Private::unbind(wl_resource *resource) { Private *o = cast(resource); if (!o) { return; } auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) { return r.resource == resource; }); if (it != o->resources.end()) { o->resources.erase(it); } } void OutputDeviceInterface::Private::sendMode(wl_resource *resource, const Mode &mode) { int32_t flags = 0; if (mode.flags.testFlag(ModeFlag::Current)) { flags |= WL_OUTPUT_MODE_CURRENT; } if (mode.flags.testFlag(ModeFlag::Preferred)) { flags |= WL_OUTPUT_MODE_PREFERRED; } org_kde_kwin_outputdevice_send_mode(resource, flags, mode.size.width(), mode.size.height(), mode.refreshRate, mode.id); } void OutputDeviceInterface::Private::sendGeometry(wl_resource *resource) { org_kde_kwin_outputdevice_send_geometry(resource, globalPosition.x(), globalPosition.y(), physicalSize.width(), physicalSize.height(), toSubPixel(), qPrintable(manufacturer), qPrintable(model), toTransform()); } void OutputDeviceInterface::Private::sendScale(const ResourceData &data) { if (wl_resource_get_version(data.resource) < ORG_KDE_KWIN_OUTPUTDEVICE_SCALEF_SINCE_VERSION) { org_kde_kwin_outputdevice_send_scale(data.resource, qRound(scale)); } else { org_kde_kwin_outputdevice_send_scalef(data.resource, wl_fixed_from_double(scale)); } } void OutputDeviceInterface::Private::sendColorCurves(const ResourceData &data) { if (data.version < ORG_KDE_KWIN_OUTPUTDEVICE_COLORCURVES_SINCE_VERSION) { return; } wl_array wlRed, wlGreen, wlBlue; auto fillArray = [](const QVector &origin, wl_array *dest) { wl_array_init(dest); const size_t memLength = sizeof(uint16_t) * origin.size(); void *s = wl_array_add(dest, memLength); memcpy(s, origin.data(), memLength); }; fillArray(colorCurves.red, &wlRed); fillArray(colorCurves.green, &wlGreen); fillArray(colorCurves.blue, &wlBlue); org_kde_kwin_outputdevice_send_colorcurves(data.resource, &wlRed, &wlGreen, &wlBlue); wl_array_release(&wlRed); wl_array_release(&wlGreen); wl_array_release(&wlBlue); } +void KWayland::Server::OutputDeviceInterface::Private::sendSerialNumber(const ResourceData &data) +{ + if (wl_resource_get_version(data.resource) >= ORG_KDE_KWIN_OUTPUTDEVICE_SERIAL_NUMBER_SINCE_VERSION) { + org_kde_kwin_outputdevice_send_serial_number(data.resource, + qPrintable(serialNumber)); + } +} + +void KWayland::Server::OutputDeviceInterface::Private::sendEisaId(const ResourceData &data) +{ + if (wl_resource_get_version(data.resource) >= ORG_KDE_KWIN_OUTPUTDEVICE_EISA_ID_SINCE_VERSION) { + org_kde_kwin_outputdevice_send_eisa_id(data.resource, + qPrintable(eisaId)); + } +} + + void OutputDeviceInterface::Private::sendDone(const ResourceData &data) { org_kde_kwin_outputdevice_send_done(data.resource); } void OutputDeviceInterface::Private::updateGeometry() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendGeometry((*it).resource); sendDone(*it); } } void OutputDeviceInterface::Private::updateScale() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendScale(*it); sendDone(*it); } } void OutputDeviceInterface::Private::updateColorCurves() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendColorCurves(*it); sendDone(*it); } } bool OutputDeviceInterface::ColorCurves::operator==(const ColorCurves &cc) const { return red == cc.red && green == cc.green && blue == cc.blue; } bool OutputDeviceInterface::ColorCurves::operator!=(const ColorCurves &cc) const { return !operator==(cc); } #define SETTER(setterName, type, argumentName) \ void OutputDeviceInterface::setterName(type arg) \ { \ Q_D(); \ if (d->argumentName == arg) { \ return; \ } \ d->argumentName = arg; \ emit argumentName##Changed(d->argumentName); \ } SETTER(setPhysicalSize, const QSize&, physicalSize) SETTER(setGlobalPosition, const QPoint&, globalPosition) SETTER(setManufacturer, const QString&, manufacturer) SETTER(setModel, const QString&, model) +SETTER(setSerialNumber, const QString&, serialNumber) +SETTER(setEisaId, const QString&, eisaId) SETTER(setSubPixel, SubPixel, subPixel) SETTER(setTransform, Transform, transform) #undef SETTER void OutputDeviceInterface::setScale(int scale) { Q_D(); if (d->scale == scale) { return; } d->scale = scale; emit scaleChanged(d->scale); emit scaleFChanged(d->scale); } void OutputDeviceInterface::setScaleF(qreal scale) { Q_D(); if (qFuzzyCompare(d->scale, scale)) { return; } d->scale = scale; emit scaleChanged(qRound(d->scale)); emit scaleFChanged(d->scale); } QSize OutputDeviceInterface::physicalSize() const { Q_D(); return d->physicalSize; } QPoint OutputDeviceInterface::globalPosition() const { Q_D(); return d->globalPosition; } QString OutputDeviceInterface::manufacturer() const { Q_D(); return d->manufacturer; } QString OutputDeviceInterface::model() const { Q_D(); return d->model; } +QString OutputDeviceInterface::serialNumber() const +{ + Q_D(); + return d->serialNumber; +} + +QString OutputDeviceInterface::eisaId() const +{ + Q_D(); + return d->eisaId; +} + int OutputDeviceInterface::scale() const { Q_D(); return qRound(d->scale); } qreal OutputDeviceInterface::scaleF() const { Q_D(); return d->scale; } OutputDeviceInterface::SubPixel OutputDeviceInterface::subPixel() const { Q_D(); return d->subPixel; } OutputDeviceInterface::Transform OutputDeviceInterface::transform() const { Q_D(); return d->transform; } OutputDeviceInterface::ColorCurves OutputDeviceInterface::colorCurves() const { Q_D(); return d->colorCurves; } QList< OutputDeviceInterface::Mode > OutputDeviceInterface::modes() const { Q_D(); return d->modes; } int OutputDeviceInterface::currentModeId() const { Q_D(); for (const Mode &m: d->modes) { if (m.flags.testFlag(OutputDeviceInterface::ModeFlag::Current)) { return m.id; } } return -1; } OutputDeviceInterface::Private *OutputDeviceInterface::d_func() const { return reinterpret_cast(d.data()); } void OutputDeviceInterface::setColorCurves(const ColorCurves &colorCurves) { Q_D(); if (d->colorCurves == colorCurves) { return; } d->colorCurves = colorCurves; emit colorCurvesChanged(d->colorCurves); } void OutputDeviceInterface::setEdid(const QByteArray &edid) { Q_D(); d->edid = edid; d->sendEdid(); emit edidChanged(); } QByteArray OutputDeviceInterface::edid() const { Q_D(); return d->edid; } void OutputDeviceInterface::setEnabled(OutputDeviceInterface::Enablement enabled) { Q_D(); if (d->enabled != enabled) { d->enabled = enabled; d->sendEnabled(); emit enabledChanged(); } } OutputDeviceInterface::Enablement OutputDeviceInterface::enabled() const { Q_D(); return d->enabled; } void OutputDeviceInterface::setUuid(const QByteArray &uuid) { Q_D(); if (d->uuid != uuid) { d->uuid = uuid; d->sendUuid(); emit uuidChanged(); } } QByteArray OutputDeviceInterface::uuid() const { Q_D(); return d->uuid; } void KWayland::Server::OutputDeviceInterface::Private::sendEdid() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_kwin_outputdevice_send_edid((*it).resource, edid.toBase64().constData()); } } void KWayland::Server::OutputDeviceInterface::Private::sendEnabled() { int _enabled = 0; if (enabled == OutputDeviceInterface::Enablement::Enabled) { _enabled = 1; } for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_kwin_outputdevice_send_enabled((*it).resource, _enabled); } } void OutputDeviceInterface::Private::sendUuid() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_kwin_outputdevice_send_uuid((*it).resource, uuid.constData()); } } } } diff --git a/src/server/outputdevice_interface.h b/src/server/outputdevice_interface.h index 1fe35a4..a9424cb 100644 --- a/src/server/outputdevice_interface.h +++ b/src/server/outputdevice_interface.h @@ -1,175 +1,183 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_SERVER_OUTPUTDEVICE_INTERFACE_H #define WAYLAND_SERVER_OUTPUTDEVICE_INTERFACE_H #include #include #include #include #include #include "global.h" struct wl_resource; namespace KWayland { namespace Server { class Display; /** @class OutputDeviceInterface * * Represents an output device, the difference to Output is that this output can be disabled, * so not currently used to display content. * * @see OutputManagementInterface * @since 5.5 */ class KWAYLANDSERVER_EXPORT OutputDeviceInterface : public Global { Q_OBJECT Q_PROPERTY(QSize physicalSize READ physicalSize WRITE setPhysicalSize NOTIFY physicalSizeChanged) Q_PROPERTY(QPoint globalPosition READ globalPosition WRITE setGlobalPosition NOTIFY globalPositionChanged) Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged) Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QString serialNumber READ serialNumber WRITE setSerialNumber NOTIFY serialNumberChanged) + Q_PROPERTY(QString eisaId READ eisaId WRITE setEisaId NOTIFY eisaIdChanged) Q_PROPERTY(QSize pixelSize READ pixelSize NOTIFY pixelSizeChanged) Q_PROPERTY(int refreshRate READ refreshRate NOTIFY refreshRateChanged) Q_PROPERTY(qreal scale READ scaleF WRITE setScaleF NOTIFY scaleFChanged) Q_PROPERTY(QByteArray edid READ edid WRITE setEdid NOTIFY edidChanged) Q_PROPERTY(OutputDeviceInterface::Enablement enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(QByteArray uuid READ uuid WRITE setUuid NOTIFY uuidChanged) public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; enum class Enablement { Disabled = 0, Enabled = 1 }; enum class ModeFlag { Current = 1, Preferred = 2 }; Q_DECLARE_FLAGS(ModeFlags, ModeFlag) struct Mode { QSize size = QSize(); int refreshRate = 60000; ModeFlags flags; int id = -1; }; struct ColorCurves { QVector red, green, blue; bool operator==(const ColorCurves &cc) const; bool operator!=(const ColorCurves &cc) const; }; virtual ~OutputDeviceInterface(); QSize physicalSize() const; QPoint globalPosition() const; QString manufacturer() const; QString model() const; + QString serialNumber() const; + QString eisaId() const; QSize pixelSize() const; int refreshRate() const; int scale() const; qreal scaleF() const; SubPixel subPixel() const; Transform transform() const; ColorCurves colorCurves() const; QList modes() const; int currentModeId() const; QByteArray edid() const; OutputDeviceInterface::Enablement enabled() const; QByteArray uuid() const; void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); + void setSerialNumber(const QString &serialNumber); + void setEisaId(const QString &eisaId); void setScale(int scale); void setScaleF(qreal scale); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void setColorCurves(const ColorCurves &colorCurves); void addMode(Mode &mode); void setCurrentMode(const int modeId); void setEdid(const QByteArray &edid); void setEnabled(OutputDeviceInterface::Enablement enabled); void setUuid(const QByteArray &uuid); static OutputDeviceInterface *get(wl_resource *native); static QListlist(); Q_SIGNALS: void physicalSizeChanged(const QSize&); void globalPositionChanged(const QPoint&); void manufacturerChanged(const QString&); void modelChanged(const QString&); + void serialNumberChanged(const QString&); + void eisaIdChanged(const QString &); void pixelSizeChanged(const QSize&); void refreshRateChanged(int); //@deprecated see scaleChanged(real) void scaleChanged(int); void scaleFChanged(qreal); void subPixelChanged(SubPixel); void transformChanged(Transform); void colorCurvesChanged(ColorCurves); void modesChanged(); void currentModeChanged(); void edidChanged(); void enabledChanged(); void uuidChanged(); private: friend class Display; explicit OutputDeviceInterface(Display *display, QObject *parent = nullptr); class Private; Private *d_func() const; }; } } Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::OutputDeviceInterface::ModeFlags) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::Enablement) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::SubPixel) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::Transform) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::ColorCurves) #endif