diff --git a/autotests/client/wlr_output_management.cpp b/autotests/client/wlr_output_management.cpp index b3a50d86..4ad8d2ac 100644 --- a/autotests/client/wlr_output_management.cpp +++ b/autotests/client/wlr_output_management.cpp @@ -167,6 +167,7 @@ void TestWlrOutputManagement::init() auto state = first_output->get_state(); state.mode = m1; state.geometry = QRectF(QPointF(0, 1920), QSizeF(1024, 768)); + state.adaptive_sync = true; first_output->set_state(state); server.globals.outputs.push_back(std::move(first_output)); server.globals.output_manager->commit_changes(); @@ -222,7 +223,7 @@ void TestWlrOutputManagement::test_add_remove_output() QCOMPARE(client.wl_output_spy->size(), 1); // The wlr head receives the changed properties. Client-side no atomic changes at the moment. - QTRY_COMPARE(head_changed_spy.size(), 5); + QTRY_COMPARE(head_changed_spy.size(), 6); // Add one more output which is already enabled. server.globals.outputs.emplace_back( @@ -282,6 +283,7 @@ void TestWlrOutputManagement::test_properties() QCOMPARE(wlr_head1->position(), QPoint(0, 0)); QCOMPARE(wlr_head1->transform(), Clt::WlrOutputHeadV1::Transform::Normal); QCOMPARE(wlr_head1->scale(), 1); + QVERIFY(!wlr_head1->adaptive_sync()); // The first output is not yet enabled. Do this now. All data is sent. auto state = server.globals.outputs.front()->get_state(); @@ -306,6 +308,7 @@ void TestWlrOutputManagement::test_properties() QCOMPARE(wlr_head1->position(), QPoint(0, 1920)); QCOMPARE(wlr_head1->transform(), Clt::WlrOutputHeadV1::Transform::Normal); QCOMPARE(wlr_head1->scale(), 1); + QVERIFY(wlr_head1->adaptive_sync()); // Add one more output which is already enabled. Srv::output_metadata meta{ @@ -322,6 +325,7 @@ void TestWlrOutputManagement::test_properties() state.enabled = true; state.geometry = QRectF(QPointF(1920, 1920), QSizeF(400, 300)); state.transform = Srv::output_transform::flipped_180; + state.adaptive_sync = true; server_output->set_state(state); server.globals.output_manager->commit_changes(); @@ -347,6 +351,7 @@ void TestWlrOutputManagement::test_properties() QCOMPARE(wlr_head2->position(), QPoint(1920, 1920)); QCOMPARE(wlr_head2->transform(), Clt::WlrOutputHeadV1::Transform::Flipped180); QCOMPARE(wlr_head2->scale(), 2); + QVERIFY(wlr_head2->adaptive_sync()); } void TestWlrOutputManagement::test_configuration() @@ -399,6 +404,7 @@ void TestWlrOutputManagement::test_configuration() QCOMPARE(wlr_head2->position(), QPoint(1920, 1920)); QCOMPARE(wlr_head2->transform(), Clt::WlrOutputHeadV1::Transform::Flipped180); QCOMPARE(wlr_head2->scale(), 2); + QVERIFY(!wlr_head2->adaptive_sync()); auto config = std::unique_ptr{ client.output_manager->createConfiguration()}; @@ -411,6 +417,7 @@ void TestWlrOutputManagement::test_configuration() config->setPosition(wlr_head2, QPoint(0, 0)); config->setTransform(wlr_head2, Clt::WlrOutputHeadV1::Transform::Rotated90); config->setScale(wlr_head2, 1); + config->set_adaptive_sync(wlr_head2, true); config->apply(); QSignalSpy config_apply_spy(server.globals.output_manager->wlr_manager_v1.get(), @@ -429,6 +436,7 @@ void TestWlrOutputManagement::test_configuration() QCOMPARE(config_head->get_state().mode, server.modes.at(1)); QCOMPARE(config_head->get_state().geometry, QRect(QPoint(0, 0), QSize(768, 1024))); QCOMPARE(config_head->get_state().transform, Srv::output_transform::rotated_90); + QVERIFY(config_head->get_state().adaptive_sync); server_config->send_succeeded(); diff --git a/server/output.h b/server/output.h index 78ef9eb8..027941c8 100644 --- a/server/output.h +++ b/server/output.h @@ -92,6 +92,7 @@ struct output_state { // Automatically calculated on setter call. int client_scale = 1; + bool adaptive_sync{false}; }; /** diff --git a/server/wlr_output_configuration_head_v1.cpp b/server/wlr_output_configuration_head_v1.cpp index 4cd43aa8..cf4236ca 100644 --- a/server/wlr_output_configuration_head_v1.cpp +++ b/server/wlr_output_configuration_head_v1.cpp @@ -24,6 +24,7 @@ struct zwlr_output_configuration_head_v1_interface const set_position_callback, set_transform_callback, set_scale_callback, + set_adaptive_sync_callback, }; bool is_portrait_transform(output_transform tr) @@ -130,6 +131,21 @@ void wlr_output_configuration_head_v1::Private::set_scale_callback(wl_client* /* priv->state.geometry.setSize(estimate_logical_size(priv->state, priv->scale)); } +void wlr_output_configuration_head_v1::Private::set_adaptive_sync_callback(wl_client* /*wlClient*/, + wl_resource* wlResource, + uint32_t wlState) +{ + auto priv = get_handle(wlResource)->d_ptr; + if (wlState != ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED + && wlState != ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED) { + priv->postError(ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE, + "adaptive sync state out of range"); + return; + } + + priv->state.adaptive_sync = wlState == ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED; +} + wlr_output_configuration_head_v1::wlr_output_configuration_head_v1( Client* client, uint32_t version, diff --git a/server/wlr_output_configuration_head_v1_p.h b/server/wlr_output_configuration_head_v1_p.h index 373c5994..abbf88c2 100644 --- a/server/wlr_output_configuration_head_v1_p.h +++ b/server/wlr_output_configuration_head_v1_p.h @@ -49,6 +49,8 @@ class wlr_output_configuration_head_v1::Private set_transform_callback(wl_client* wlClient, wl_resource* wlResource, int32_t wlTransform); static void set_scale_callback(wl_client* wlClient, wl_resource* wlResource, wl_fixed_t wlScale); + static void + set_adaptive_sync_callback(wl_client* wlClient, wl_resource* wlResource, uint32_t wlState); static const struct zwlr_output_configuration_head_v1_interface s_interface; }; diff --git a/server/wlr_output_head_v1.cpp b/server/wlr_output_head_v1.cpp index 8c6790c5..a2726a08 100644 --- a/server/wlr_output_head_v1.cpp +++ b/server/wlr_output_head_v1.cpp @@ -108,6 +108,13 @@ void wlr_output_head_v1::broadcast() current_scale = scale; manager.d_ptr->changed = true; } + + if (published.state.adaptive_sync != pending.state.adaptive_sync || !published.state.enabled) { + for (auto res : resources) { + res->send_adaptive_sync(pending.state.adaptive_sync); + } + manager.d_ptr->changed = true; + } } wlr_output_head_v1_res::Private::Private(Client* client, @@ -172,6 +179,7 @@ void wlr_output_head_v1_res::send_mutable_data(output_state const& data) const send_position(data.geometry.topLeft().toPoint()); send_transform(data.transform); send_scale(estimate_scale(data)); + send_adaptive_sync(data.adaptive_sync); } void wlr_output_head_v1_res::send_enabled(bool enabled) const @@ -203,4 +211,14 @@ void wlr_output_head_v1_res::send_scale(double scale) const d_ptr->send(wl_fixed_from_double(scale)); } +void wlr_output_head_v1_res::send_adaptive_sync(bool is_adaptive) const +{ + if (d_ptr->version < ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_SINCE_VERSION) { + return; + } + d_ptr->send( + is_adaptive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED + : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); +} + } diff --git a/server/wlr_output_head_v1_p.h b/server/wlr_output_head_v1_p.h index 6ac28e6d..e903d3e9 100644 --- a/server/wlr_output_head_v1_p.h +++ b/server/wlr_output_head_v1_p.h @@ -55,6 +55,7 @@ class wlr_output_head_v1_res : public QObject void send_position(QPoint const& pos) const; void send_transform(output_transform transform) const; void send_scale(double scale) const; + void send_adaptive_sync(bool is_adaptive) const; class Private; Private* d_ptr; diff --git a/server/wlr_output_manager_v1_p.h b/server/wlr_output_manager_v1_p.h index 930e48e5..bfbacb6c 100644 --- a/server/wlr_output_manager_v1_p.h +++ b/server/wlr_output_manager_v1_p.h @@ -19,7 +19,7 @@ namespace Wrapland::Server class wlr_output_configuration_v1; class wlr_output_head_v1; -constexpr uint32_t wlr_output_manager_v1_version = 3; +constexpr uint32_t wlr_output_manager_v1_version = 4; using wlr_output_manager_v1_global = Wayland::Global; using wlr_output_manager_v1_bind = Wayland::Bind; diff --git a/src/client/protocols/wlr-output-management-unstable-v1.xml b/src/client/protocols/wlr-output-management-unstable-v1.xml index 3568e04c..411e2f04 100644 --- a/src/client/protocols/wlr-output-management-unstable-v1.xml +++ b/src/client/protocols/wlr-output-management-unstable-v1.xml @@ -39,7 +39,7 @@ interface version number is reset. - + This interface is a manager that allows reading and writing the current output device configuration. @@ -125,7 +125,7 @@ - + A head is an output device. The difference between a wl_output object and a head is that heads are advertised even if they are turned off. A head @@ -338,6 +338,22 @@ object. + + + + + + + + + + + This event describes whether adaptive sync is currently enabled for + the head or not. Adaptive sync is also known as Variable Refresh + Rate or VRR. + + + @@ -395,7 +411,7 @@ - + This object is used by the client to describe a full output configuration. @@ -513,7 +529,7 @@ - + This object is used by the client to update a single head's configuration. @@ -526,6 +542,8 @@ + @@ -569,5 +587,15 @@ + + + + + + This request enables/disables adaptive sync. Adaptive sync is also + known as Variable Refresh Rate or VRR. + + + diff --git a/src/client/registry.cpp b/src/client/registry.cpp index bfa973d3..e25c64f4 100644 --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -330,7 +330,7 @@ static QMap const s_interfaces = { { Registry::Interface::WlrOutputManagerV1, { - 3, + 4, QByteArrayLiteral("zwlr_output_manager_v1"), &zwlr_output_manager_v1_interface, &Registry::wlrOutputManagerV1Announced, diff --git a/src/client/wlr_output_configuration_v1.cpp b/src/client/wlr_output_configuration_v1.cpp index 6f219352..13ab803a 100644 --- a/src/client/wlr_output_configuration_v1.cpp +++ b/src/client/wlr_output_configuration_v1.cpp @@ -50,6 +50,8 @@ struct ConfigurationHead { bool transformSet = false; double scale = 1.; bool scaleSet = false; + bool adapt_sync{false}; + bool adapt_sync_set{false}; }; class Q_DECL_HIDDEN WlrOutputConfigurationV1::Private @@ -200,6 +202,13 @@ void WlrOutputConfigurationV1::Private::send() zwlr_output_configuration_head_v1_set_scale(head->native, wl_fixed_from_double(head->scale)); } + + if (head->adapt_sync_set) { + zwlr_output_configuration_head_v1_set_adaptive_sync( + head->native, + head->adapt_sync ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED + : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); + } } } @@ -277,6 +286,18 @@ void WlrOutputConfigurationV1::setScale(WlrOutputHeadV1* head, double scale) configurationHead->scaleSet = true; } +void WlrOutputConfigurationV1::set_adaptive_sync(WlrOutputHeadV1* head, bool enable) +{ + auto configurationHead = d->getConfigurationHead(head); + if (zwlr_output_configuration_head_v1_get_version(configurationHead->native) + < ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_SET_ADAPTIVE_SYNC_SINCE_VERSION) { + return; + } + + configurationHead->adapt_sync = enable; + configurationHead->adapt_sync_set = true; +} + void WlrOutputConfigurationV1::test() { d->send(); diff --git a/src/client/wlr_output_configuration_v1.h b/src/client/wlr_output_configuration_v1.h index 4dd3ca5f..891f11c6 100644 --- a/src/client/wlr_output_configuration_v1.h +++ b/src/client/wlr_output_configuration_v1.h @@ -185,6 +185,8 @@ class WRAPLANDCLIENT_EXPORT WlrOutputConfigurationV1 : public QObject */ void setScale(WlrOutputHeadV1* head, double scale); + void set_adaptive_sync(WlrOutputHeadV1* head, bool enable); + /** * Check if current configuration is valid. Either succeeded() or failed() will be emitted * later on as a result of this call. diff --git a/src/client/wlr_output_manager_v1.cpp b/src/client/wlr_output_manager_v1.cpp index 40f2d241..4648cad0 100644 --- a/src/client/wlr_output_manager_v1.cpp +++ b/src/client/wlr_output_manager_v1.cpp @@ -285,6 +285,7 @@ class Q_DECL_HIDDEN WlrOutputHeadV1::Private static void modelCallback(void* data, zwlr_output_head_v1* head, char const* model); static void serialNumberCallback(void* data, zwlr_output_head_v1* head, char const* serialNumber); + static void adaptive_sync_callback(void* data, zwlr_output_head_v1* head, uint32_t state); WlrOutputHeadV1* q; static const struct zwlr_output_head_v1_listener s_listener; @@ -296,6 +297,7 @@ class Q_DECL_HIDDEN WlrOutputHeadV1::Private Transform transform{Transform::Normal}; bool enabled{false}; double scale{1.}; + bool adapt_sync{false}; QString make; QString model; @@ -322,6 +324,7 @@ const zwlr_output_head_v1_listener WlrOutputHeadV1::Private::s_listener = { makeCallback, modelCallback, serialNumberCallback, + adaptive_sync_callback, }; void WlrOutputHeadV1::Private::nameCallback(void* data, zwlr_output_head_v1* head, char const* name) @@ -514,6 +517,17 @@ void WlrOutputHeadV1::Private::serialNumberCallback(void* data, Q_EMIT d->q->changed(); } +void WlrOutputHeadV1::Private::adaptive_sync_callback(void* data, + zwlr_output_head_v1* head, + uint32_t state) +{ + auto d = reinterpret_cast(data); + Q_ASSERT(d->outputHead == head); + + d->adapt_sync = state == ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED; + Q_EMIT d->q->changed(); +} + WlrOutputHeadV1::Private::Private(WlrOutputHeadV1* q, zwlr_output_head_v1* head) : q(q) { @@ -579,6 +593,11 @@ double WlrOutputHeadV1::scale() const return d->scale; } +bool WlrOutputHeadV1::adaptive_sync() const +{ + return d->adapt_sync; +} + QVector WlrOutputHeadV1::modes() const { QVector ret; diff --git a/src/client/wlr_output_manager_v1.h b/src/client/wlr_output_manager_v1.h index 2cda6b85..1f55ead2 100644 --- a/src/client/wlr_output_manager_v1.h +++ b/src/client/wlr_output_manager_v1.h @@ -193,6 +193,7 @@ class WRAPLANDCLIENT_EXPORT WlrOutputHeadV1 : public QObject QPoint position() const; Transform transform() const; double scale() const; + bool adaptive_sync() const; operator zwlr_output_head_v1*(); operator zwlr_output_head_v1*() const;