diff --git a/autotests/client/test_wayland_outputdevice.cpp b/autotests/client/test_wayland_outputdevice.cpp --- a/autotests/client/test_wayland_outputdevice.cpp +++ b/autotests/client/test_wayland_outputdevice.cpp @@ -56,6 +56,8 @@ void testEdid(); void testId(); void testDone(); + void testSupportedTransformation_data(); + void testSupportedTransformation(); private: KWayland::Server::Display *m_display; @@ -186,6 +188,7 @@ QCOMPARE(output.transform(), KWayland::Client::OutputDevice::Transform::Normal); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); QCOMPARE(output.edid(), QByteArray()); + QCOMPARE(output.supportedTransformations(), QVector()); QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); @@ -206,6 +209,7 @@ QCOMPARE(output.subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown); // for xwayland transform is normal QCOMPARE(output.transform(), KWayland::Client::OutputDevice::Transform::Normal); + QCOMPARE(output.supportedTransformations(), QVector{KWayland::Client::OutputDevice::Transform::Normal}); QCOMPARE(output.edid(), m_edid); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); @@ -569,5 +573,64 @@ } +void TestWaylandOutputDevice::testSupportedTransformation_data() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + QTest::addColumn("transformation"); + QTest::addColumn("serverTransformation"); + QTest::addColumn("expectedCount"); + + QTest::addRow("normal") << OutputDevice::Transform::Normal << OutputDeviceInterface::Transform::Normal << 1; + QTest::addRow("Rotated90") << OutputDevice::Transform::Rotated90 << OutputDeviceInterface::Transform::Rotated90 << 2; + QTest::addRow("Rotated180") << OutputDevice::Transform::Rotated180 << OutputDeviceInterface::Transform::Rotated180 << 2; + QTest::addRow("Rotated270") << OutputDevice::Transform::Rotated270 << OutputDeviceInterface::Transform::Rotated270 << 2; + QTest::addRow("Flipped") << OutputDevice::Transform::Flipped << OutputDeviceInterface::Transform::Flipped << 2; + QTest::addRow("Flipped90") << OutputDevice::Transform::Flipped90 << OutputDeviceInterface::Transform::Flipped90 << 2; + QTest::addRow("Flipped180") << OutputDevice::Transform::Flipped180 << OutputDeviceInterface::Transform::Flipped180 << 2; + QTest::addRow("Flipped270") << OutputDevice::Transform::Flipped270 << OutputDeviceInterface::Transform::Flipped270 << 2; +} + +void TestWaylandOutputDevice::testSupportedTransformation() +{ + using namespace KWayland::Client; + using namespace KWayland::Server; + Registry registry; + QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); + QVERIFY(interfacesAnnouncedSpy.isValid()); + QSignalSpy announced(®istry, &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()); + + QFETCH(OutputDeviceInterface::Transform, serverTransformation); + m_serverOutputDevice->addSupportedTransformation(serverTransformation); + + QScopedPointer output{registry.createOutputDevice(announced.first().first().value(), announced.first().last().value())}; + QSignalSpy outputDone(output.data(), &OutputDevice::done); + QVERIFY(outputDone.isValid()); + + QSignalSpy supportedTransformationAddedSpy(output.data(), &OutputDevice::supportedTransformationAdded); + QVERIFY(supportedTransformationAddedSpy.isValid()); + QVERIFY(supportedTransformationAddedSpy.wait()); + QFETCH(int, expectedCount); + QTRY_COMPARE(supportedTransformationAddedSpy.count(), expectedCount); + + QCOMPARE(supportedTransformationAddedSpy.first().first().value(), OutputDevice::Transform::Normal); + QFETCH(OutputDevice::Transform, transformation); + QCOMPARE(supportedTransformationAddedSpy.last().first().value(), transformation); + + QTRY_COMPARE(outputDone.count(), 1); + QCOMPARE(output->supportedTransformations().count(), expectedCount); + QVector supportedTransformation{OutputDevice::Transform::Normal}; + if (expectedCount > 1) { + supportedTransformation << transformation; + } + QCOMPARE(output->supportedTransformations(), supportedTransformation); +} + QTEST_GUILESS_MAIN(TestWaylandOutputDevice) #include "test_wayland_outputdevice.moc" diff --git a/src/client/outputdevice.h b/src/client/outputdevice.h --- a/src/client/outputdevice.h +++ b/src/client/outputdevice.h @@ -239,6 +239,13 @@ **/ void destroy(); + /** + * @returns The transformations supported on this OutputDevice + * @since 5.XX + * @see supportedTransformationAdded + **/ + QVector supportedTransformations() const; + Q_SIGNALS: /** * Emitted when the output is fully initialized. @@ -280,6 +287,13 @@ **/ void removed(); + /** + * Emitted whenever a supported @p transform is added. + * @since 5.XX + * @see supportedTransformations + **/ + void supportedTransformationAdded(KWayland::Client::OutputDevice::Transform transform); + private: class Private; QScopedPointer d; diff --git a/src/client/outputdevice.cpp b/src/client/outputdevice.cpp --- a/src/client/outputdevice.cpp +++ b/src/client/outputdevice.cpp @@ -59,6 +59,7 @@ OutputDevice::Enablement enabled = OutputDevice::Enablement::Enabled; QByteArray uuid; bool done = false; + QVector supportedTransformations; private: static void geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, @@ -71,6 +72,7 @@ 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 supportedTransformationCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t transformation); void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); @@ -95,6 +97,9 @@ Q_ASSERT(o); Q_ASSERT(!output); output.setup(o); + if (org_kde_kwin_outputdevice_get_version(output) < ORG_KDE_KWIN_OUTPUTDEVICE_SUPPORTED_TRANSFORMATION_SINCE_VERSION) { + supportedTransformations << Transform::Normal; + } org_kde_kwin_outputdevice_add_listener(output, &s_outputListener, this); } @@ -124,9 +129,35 @@ scaleCallback, edidCallback, enabledCallback, - uuidCallback + uuidCallback, + supportedTransformationCallback }; +namespace { +OutputDevice::Transform toTransform(int32_t transform) +{ + switch (transform) { + case WL_OUTPUT_TRANSFORM_90: + return OutputDevice::Transform::Rotated90; + case WL_OUTPUT_TRANSFORM_180: + return OutputDevice::Transform::Rotated180; + case WL_OUTPUT_TRANSFORM_270: + return OutputDevice::Transform::Rotated270; + case WL_OUTPUT_TRANSFORM_FLIPPED: + return OutputDevice::Transform::Flipped; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + return OutputDevice::Transform::Flipped90; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + return OutputDevice::Transform::Flipped180; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + return OutputDevice::Transform::Flipped270; + case WL_OUTPUT_TRANSFORM_NORMAL: + default: + return OutputDevice::Transform::Normal; + } +} +} + void OutputDevice::Private::geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, @@ -157,28 +188,7 @@ } }; 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()); + o->setTransform(toTransform(transform)); } 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) @@ -292,6 +302,20 @@ } } +void OutputDevice::Private::supportedTransformationCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t transformation) +{ + Q_UNUSED(output); + auto o = reinterpret_cast(data); + const auto t = toTransform(transformation); + if (!o->supportedTransformations.contains(t)) { + o->supportedTransformations << t; + emit o->q->supportedTransformationAdded(t); + if (o->done) { + emit o->q->changed(); + } + } +} + void OutputDevice::setup(org_kde_kwin_outputdevice *output) { d->setup(output); @@ -445,5 +469,10 @@ } +QVector OutputDevice::supportedTransformations() const +{ + return d->supportedTransformations; +} + } } diff --git a/src/client/protocols/outputdevice.xml b/src/client/protocols/outputdevice.xml --- a/src/client/protocols/outputdevice.xml +++ b/src/client/protocols/outputdevice.xml @@ -29,7 +29,7 @@ ]]> - + An outputdevice describes a physical monitor connected to the system. outputdevice is similar to wl_output, but focuses on screen @@ -217,6 +217,14 @@ + + + This event is sent for each transformation supported on the outputdevice. + The argument is of the enum type transform describing one supported transformation. + + + + diff --git a/src/client/registry.cpp b/src/client/registry.cpp --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -186,7 +186,7 @@ &Registry::outputManagementRemoved }}, {Registry::Interface::OutputDevice, { - 1, + 2, QByteArrayLiteral("org_kde_kwin_outputdevice"), &org_kde_kwin_outputdevice_interface, &Registry::outputDeviceAnnounced, diff --git a/src/server/outputdevice_interface.h b/src/server/outputdevice_interface.h --- a/src/server/outputdevice_interface.h +++ b/src/server/outputdevice_interface.h @@ -123,6 +123,17 @@ void setEnabled(OutputDeviceInterface::Enablement enabled); void setUuid(const QByteArray &uuid); + /** + * Adds @p transform to the supported transformations on this OutputDeviceInterface. + * Please note that Transform::Normal is always considered supported for + * backward compatibility. + * + * Supported transformations are static during the life-time of the OutputDeviceInterface. + * Due to that there is no remove operation for supported transformations. + * @since 5.XX + **/ + void addSupportedTransformation(Transform transform); + static OutputDeviceInterface *get(wl_resource *native); static QListlist(); diff --git a/src/server/outputdevice_interface.cpp b/src/server/outputdevice_interface.cpp --- a/src/server/outputdevice_interface.cpp +++ b/src/server/outputdevice_interface.cpp @@ -49,6 +49,7 @@ void sendUuid(); void sendEdid(); void sendEnabled(); + void sendSupportedTransformation(const ResourceData &data, Transform t); QSize physicalSize; QPoint globalPosition; @@ -64,23 +65,26 @@ Enablement enabled = Enablement::Enabled; QByteArray uuid; + QVector supportedTransformations = QVector{Transform::Normal}; + 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 toTransform(Transform t) const; int32_t toSubPixel() const; void sendGeometry(wl_resource *resource); void sendScale(const ResourceData &data); + void sendSupportedTransformations(const ResourceData &resource); static const quint32 s_version; OutputDeviceInterface *q; static QVector s_privates; }; -const quint32 OutputDeviceInterface::Private::s_version = 1; +const quint32 OutputDeviceInterface::Private::s_version = 2; QVector OutputDeviceInterface::Private::s_privates; @@ -274,7 +278,7 @@ emit currentModeChanged(); } -int32_t OutputDeviceInterface::Private::toTransform() const +int32_t OutputDeviceInterface::Private::toTransform(OutputDeviceInterface::Transform transform) const { switch (transform) { case Transform::Normal: @@ -333,6 +337,7 @@ sendGeometry(resource); sendScale(r); + sendSupportedTransformations(r); auto currentModeIt = modes.constEnd(); for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) { @@ -397,7 +402,22 @@ toSubPixel(), qPrintable(manufacturer), qPrintable(model), - toTransform()); + toTransform(transform)); +} + +void OutputDeviceInterface::Private::sendSupportedTransformation(const ResourceData &data, OutputDeviceInterface::Transform t) +{ + if (data.version < ORG_KDE_KWIN_OUTPUTDEVICE_SUPPORTED_TRANSFORMATION_SINCE_VERSION) { + return; + } + org_kde_kwin_outputdevice_send_supported_transformation(data.resource, toTransform(t)); +} + +void OutputDeviceInterface::Private::sendSupportedTransformations(const ResourceData &resource) +{ + for (auto t : qAsConst(supportedTransformations)) { + sendSupportedTransformation(resource, t); + } } void OutputDeviceInterface::Private::sendScale(const ResourceData &data) @@ -447,6 +467,23 @@ #undef SETTER +void OutputDeviceInterface::addSupportedTransformation(Transform transformation) +{ + Q_D(); + if (d->supportedTransformations.contains(transformation)) { + return; + } + d->supportedTransformations << transformation; + if (d->resources.isEmpty()) { + return; + } + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + d->sendSupportedTransformation(*it, transformation); + d->sendDone(*it); + } + wl_display_flush_clients(*(d->display)); +} + QSize OutputDeviceInterface::physicalSize() const { Q_D();