diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index 3267ad801a..f43f513878 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -30,10 +30,10 @@ set(SERVER_LIB_SRCS layershell_v1_interface.cpp linuxdmabufv1clientbuffer.cpp output_interface.cpp - outputchangeset.cpp - outputconfiguration_interface.cpp - outputdevice_interface.cpp - outputmanagement_interface.cpp + outputdevice_v2_interface.cpp + outputconfiguration_v2_interface.cpp + outputmanagement_v2_interface.cpp + outputchangeset_v2.cpp plasmashell_interface.cpp plasmavirtualdesktop_interface.cpp plasmawindowmanagement_interface.cpp @@ -84,13 +84,13 @@ ecm_add_qtwayland_server_protocol_kde(SERVER_LIB_SRCS ) ecm_add_qtwayland_server_protocol_kde(SERVER_LIB_SRCS - PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/output-management.xml - BASENAME outputmanagement + PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml + BASENAME kde-output-device-v2 ) ecm_add_qtwayland_server_protocol_kde(SERVER_LIB_SRCS - PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/outputdevice.xml - BASENAME org-kde-kwin-outputdevice + PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml + BASENAME kde-output-management-v2 ) ecm_add_qtwayland_server_protocol_kde(SERVER_LIB_SRCS @@ -349,10 +349,10 @@ set(SERVER_LIB_HEADERS layershell_v1_interface.h linuxdmabufv1clientbuffer.h output_interface.h - outputchangeset.h - outputconfiguration_interface.h - outputdevice_interface.h - outputmanagement_interface.h + outputchangeset_v2.h + outputconfiguration_v2_interface.h + outputdevice_v2_interface.h + outputmanagement_v2_interface.h plasmashell_interface.h plasmavirtualdesktop_interface.h plasmawindowmanagement_interface.h diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt index 2c29ae0faf..3a1dfbe13a 100644 --- a/src/wayland/autotests/client/CMakeLists.txt +++ b/src/wayland/autotests/client/CMakeLists.txt @@ -44,28 +44,6 @@ target_link_libraries( testShmPool Qt::Test Qt::Gui KF5::WaylandClient Plasma::K add_test(NAME kwayland-testShmPool COMMAND testShmPool) ecm_mark_as_test(testShmPool) -######################################################## -# Test KWin OutputManagement -######################################################## -set( test_wayland_outputmanagement_SRCS - test_wayland_outputmanagement.cpp - ) -add_executable(testWaylandOutputManagement ${test_wayland_outputmanagement_SRCS}) -target_link_libraries( testWaylandOutputManagement Qt::Test Qt::Gui KF5::WaylandClient Plasma::KWaylandServer Wayland::Client) -add_test(NAME kwayland-testWaylandOutputManagement COMMAND testWaylandOutputManagement) -ecm_mark_as_test(testWaylandOutputManagement) - -######################################################## -# Test KWin OutputDevice -######################################################## -set( test_wayland_outputdevice_SRCS - test_wayland_outputdevice.cpp - ) -add_executable(testWaylandOutputDevice ${test_wayland_outputdevice_SRCS}) -target_link_libraries( testWaylandOutputDevice Qt::Test Qt::Gui KF5::WaylandClient Plasma::KWaylandServer Wayland::Client) -add_test(NAME kwayland-testWaylandOutputDevice COMMAND testWaylandOutputDevice) -ecm_mark_as_test(testWaylandOutputDevice) - ######################################################## # Test Compositor ######################################################## diff --git a/src/wayland/autotests/server/test_display.cpp b/src/wayland/autotests/server/test_display.cpp index 7da4771212..a4aa0e5bce 100644 --- a/src/wayland/autotests/server/test_display.cpp +++ b/src/wayland/autotests/server/test_display.cpp @@ -10,6 +10,7 @@ #include "../../src/server/display.h" #include "../../src/server/output_interface.h" #include "../../src/server/outputmanagement_interface.h" +#include "../../src/server/outputmanagement_v2_interface.h" // Wayland #include // system @@ -183,7 +184,7 @@ void TestWaylandServerDisplay::testOutputManagement() Display display; display.addSocketName("kwayland-test-0"); display.start(); - new OutputManagementInterface(&display, this); + new OutputManagementV2Interface(&display, this); } void TestWaylandServerDisplay::testAutoSocketName() diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index e129e7f3ac..e27b8c8672 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -157,9 +157,9 @@ QList Display::outputs() const return d->outputs; } -QList Display::outputDevices() const +QList< OutputDeviceV2Interface* > Display::outputDevices() const { - return d->outputdevices; + return d->outputdevicesV2; } QVector Display::outputsIntersecting(const QRect &rect) const diff --git a/src/wayland/display.h b/src/wayland/display.h index bbbaed5368..75cc4d6526 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -36,7 +36,7 @@ class ClientBuffer; class ClientConnection; class DisplayPrivate; class OutputInterface; -class OutputDeviceInterface; +class OutputDeviceV2Interface; class SeatInterface; /** @@ -111,8 +111,8 @@ public: /** * @returns All SeatInterface currently managed on the Display. */ - QVector seats() const; - QList outputDevices() const; + QVector seats() const; + QList outputDevices() const; QList outputs() const; QVector outputsIntersecting(const QRect &rect) const; diff --git a/src/wayland/display_p.h b/src/wayland/display_p.h index fbfd53643b..d07556a697 100644 --- a/src/wayland/display_p.h +++ b/src/wayland/display_p.h @@ -26,7 +26,7 @@ class ClientBuffer; class ClientConnection; class Display; class OutputInterface; -class OutputDeviceInterface; +class OutputDeviceV2Interface; class SeatInterface; struct ClientBufferDestroyListener; @@ -47,7 +47,7 @@ public: wl_event_loop *loop = nullptr; bool running = false; QList outputs; - QList outputdevices; + QList outputdevicesV2; QVector seats; QVector clients; QStringList socketNames; diff --git a/src/wayland/outputchangeset_v2.cpp b/src/wayland/outputchangeset_v2.cpp new file mode 100644 index 0000000000..5050b91139 --- /dev/null +++ b/src/wayland/outputchangeset_v2.cpp @@ -0,0 +1,114 @@ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + SPDX-FileCopyrightText: 2021 Méven Car + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "outputchangeset_v2.h" +#include "outputchangeset_v2_p.h" + +namespace KWaylandServer +{ + +OutputChangeSetV2Private::OutputChangeSetV2Private(OutputDeviceV2Interface *outputdevice, OutputChangeSetV2 *parent) + : q(parent) + , outputDevice(outputdevice) + , enabled(outputDevice->enabled()) + , size(outputDevice->pixelSize()) + , refreshRate(outputDevice->refreshRate()) + , transform(outputDevice->transform()) + , position(outputDevice->globalPosition()) + , scale(outputDevice->scale()) + , overscan(outputDevice->overscan()) +{ +} + +OutputChangeSetV2::OutputChangeSetV2(OutputDeviceV2Interface *outputdevice, QObject *parent) + : QObject(parent) + , d(new OutputChangeSetV2Private(outputdevice, this)) +{ +} + +OutputChangeSetV2::~OutputChangeSetV2() = default; + +bool OutputChangeSetV2::enabledChanged() const +{ + return d->enabled != d->outputDevice->enabled(); +} + +bool OutputChangeSetV2::enabled() const +{ + return d->enabled; +} + +QSize OutputChangeSetV2::size() const +{ + return d->size; +} + +bool OutputChangeSetV2::sizeChanged() const +{ + return d->size != d->outputDevice->pixelSize(); +} + +int OutputChangeSetV2::refreshRate() const +{ + return d->refreshRate; +} + +bool OutputChangeSetV2::refreshRateChanged() const +{ + return d->refreshRate != d->outputDevice->refreshRate(); +} + +bool OutputChangeSetV2::transformChanged() const +{ + return d->transform != d->outputDevice->transform(); +} + +OutputDeviceV2Interface::Transform OutputChangeSetV2::transform() const +{ + return d->transform; +} +bool OutputChangeSetV2::positionChanged() const +{ + return d->position != d->outputDevice->globalPosition(); +} + +QPoint OutputChangeSetV2::position() const +{ + return d->position; +} + +bool OutputChangeSetV2::scaleChanged() const +{ + return !qFuzzyCompare(d->scale, d->outputDevice->scale()); +} + +qreal OutputChangeSetV2::scale() const +{ + return d->scale; +} + +bool OutputChangeSetV2::overscanChanged() const +{ + return d->overscan != d->outputDevice->overscan(); +} + +uint32_t OutputChangeSetV2::overscan() const +{ + return d->overscan; +} + +bool OutputChangeSetV2::vrrPolicyChanged() const +{ + return d->vrrPolicy != d->outputDevice->vrrPolicy(); +} + +OutputDeviceV2Interface::VrrPolicy OutputChangeSetV2::vrrPolicy() const +{ + return d->vrrPolicy; +} + +} diff --git a/src/wayland/outputchangeset_v2.h b/src/wayland/outputchangeset_v2.h new file mode 100644 index 0000000000..57276668d3 --- /dev/null +++ b/src/wayland/outputchangeset_v2.h @@ -0,0 +1,107 @@ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + SPDX-FileCopyrightText: 2021 Méven Car + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#pragma once + +#include + +#include "outputdevice_v2_interface.h" +#include + +namespace KWaylandServer +{ + +class OutputChangeSetV2Private; + +/** + * @brief Holds a set of changes to an OutputInterface or OutputDeviceInterface. + * + * This class implements a set of changes that the compositor can apply to an + * OutputInterface after OutputConfiguration::apply has been called on the client + * side. The changes are per-configuration. + * + * @see OutputConfiguration + */ +class KWAYLANDSERVER_EXPORT OutputChangeSetV2 : public QObject +{ + Q_OBJECT +public: + ~OutputChangeSetV2() override; + /** Whether the enabled() property of the outputdevice changed. + * @returns @c true if the enabled property of the outputdevice has changed. + */ + bool enabledChanged() const; + + /** Whether the transform() property of the outputdevice changed. + * @returns @c true if the enabled property of the outputdevice has changed. + * bool modeChanged() const; + */ + /** Whether the transform() property of the outputdevice changed. */ + bool transformChanged() const; + + /** Whether the size property of the outputdevice changed. + */ + bool sizeChanged() const; + + /** Whether the refreshRate property of the outputdevice changed. + */ + bool refreshRateChanged() const; + + /** Whether the globalPosition() property of the outputdevice changed. + * @returns @c true if the globalPosition() property of the outputdevice has changed. + */ + bool positionChanged() const; + + /** Whether the scale() property of the outputdevice changed. + * @returns @c true if the scale() property of the outputdevice has changed. + */ + bool scaleChanged() const; + + /** Whether the overscan() property of the outputdevice changed. + * @returns @c true if the overscan() property of the outputdevice has changed + */ + bool overscanChanged() const; + + /** + * Whether the vrrPolicy() property of the outputdevice changed. + * @returns @c true if the vrrPolicy() property of the outputdevice has changed. + */ + bool vrrPolicyChanged() const; + + /** The new value for enabled. */ + bool enabled() const; + + /** The new size */ + QSize size() const; + + /** The new refresh rate */ + int refreshRate() const; + + /** The new value for transform. */ + OutputDeviceV2Interface::Transform transform() const; + + /** The new value for globalPosition. */ + QPoint position() const; + + /** The new value for scale. + */ + qreal scale() const; + + /** the overscan value in % */ + uint32_t overscan() const; + + /** The new value for vrrPolicy */ + OutputDeviceV2Interface::VrrPolicy vrrPolicy() const; + +private: + friend class OutputConfigurationV2InterfacePrivate; + explicit OutputChangeSetV2(OutputDeviceV2Interface *outputdevice, QObject *parent = nullptr); + + QScopedPointer d; +}; + +} diff --git a/src/wayland/outputchangeset_v2_p.h b/src/wayland/outputchangeset_v2_p.h new file mode 100644 index 0000000000..9802744c99 --- /dev/null +++ b/src/wayland/outputchangeset_v2_p.h @@ -0,0 +1,31 @@ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#pragma once + +#include "outputchangeset_v2.h" + +namespace KWaylandServer +{ + +class OutputChangeSetV2Private +{ +public: + OutputChangeSetV2Private(OutputDeviceV2Interface *outputdevice, OutputChangeSetV2 *parent); + + OutputChangeSetV2 *q; + OutputDeviceV2Interface *outputDevice; + + bool enabled; + QSize size; + int refreshRate; + OutputDeviceV2Interface::Transform transform; + QPoint position; + qreal scale; + uint32_t overscan; + OutputDeviceV2Interface::VrrPolicy vrrPolicy = OutputDeviceV2Interface::VrrPolicy::Automatic; +}; + +} diff --git a/src/wayland/outputconfiguration_v2_interface.cpp b/src/wayland/outputconfiguration_v2_interface.cpp new file mode 100644 index 0000000000..db3520115b --- /dev/null +++ b/src/wayland/outputconfiguration_v2_interface.cpp @@ -0,0 +1,238 @@ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Sebastian Kügler + SPDX-FileCopyrightText: 2021 Méven Car + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#include "display.h" +#include "outputconfiguration_v2_interface.h" +#include "outputdevice_v2_interface.h" +#include "logging.h" +#include "outputchangeset_v2_p.h" + +#include "qwayland-server-kde-output-management-v2.h" +#include "qwayland-server-kde-output-device-v2.h" + +namespace KWaylandServer +{ +class OutputConfigurationV2InterfacePrivate : public QtWaylandServer::kde_output_configuration_v2 +{ +public: + OutputConfigurationV2InterfacePrivate(OutputConfigurationV2Interface *q, OutputManagementV2Interface *outputManagement, wl_resource *resource); + + void sendApplied(); + void sendFailed(); + void emitConfigurationChangeRequested() const; + void clearPendingChanges(); + + bool hasPendingChanges(OutputDeviceV2Interface *outputdevice) const; + OutputChangeSetV2 *pendingChanges(OutputDeviceV2Interface *outputdevice); + + OutputManagementV2Interface *outputManagement; + QHash changes; + OutputConfigurationV2Interface *q; + +protected: + void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override; + void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override; + void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override; + void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override; + void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override; + void kde_output_configuration_v2_apply(Resource *resource) override; + void kde_output_configuration_v2_destroy(Resource *resource) override; + void kde_output_configuration_v2_destroy_resource(Resource *resource) override; + void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override; + void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override; +}; + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) +{ + Q_UNUSED(resource) + + OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); + pendingChanges(output)->d->enabled = enable == 1; +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_mode(Resource *resource, wl_resource *outputdevice, wl_resource *modeResource) +{ + Q_UNUSED(resource) + OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); + OutputDeviceModeV2Interface *mode = OutputDeviceModeV2Interface::get(modeResource); + + pendingChanges(output)->d->size = mode->size(); + pendingChanges(output)->d->refreshRate = mode->refreshRate(); +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) +{ + Q_UNUSED(resource) + auto toTransform = [transform]() { + switch (transform) { + case WL_OUTPUT_TRANSFORM_90: + return OutputDeviceV2Interface::Transform::Rotated90; + case WL_OUTPUT_TRANSFORM_180: + return OutputDeviceV2Interface::Transform::Rotated180; + case WL_OUTPUT_TRANSFORM_270: + return OutputDeviceV2Interface::Transform::Rotated270; + case WL_OUTPUT_TRANSFORM_FLIPPED: + return OutputDeviceV2Interface::Transform::Flipped; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + return OutputDeviceV2Interface::Transform::Flipped90; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + return OutputDeviceV2Interface::Transform::Flipped180; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + return OutputDeviceV2Interface::Transform::Flipped270; + case WL_OUTPUT_TRANSFORM_NORMAL: + default: + return OutputDeviceV2Interface::Transform::Normal; + } + }; + auto _transform = toTransform(); + OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); + pendingChanges(output)->d->transform = _transform; +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) +{ + Q_UNUSED(resource) + auto _pos = QPoint(x, y); + OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); + pendingChanges(output)->d->position = _pos; +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) +{ + Q_UNUSED(resource) + const qreal doubleScale = wl_fixed_to_double(scale); + + if (doubleScale <= 0) { + qCWarning(KWAYLAND_SERVER) << "Requested to scale output device to" << doubleScale << ", but I can't do that."; + return; + } + OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); + + pendingChanges(output)->d->scale = doubleScale; +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_apply(Resource *resource) +{ + Q_UNUSED(resource) + emitConfigurationChangeRequested(); +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) +{ + Q_UNUSED(resource) + if (overscan > 100) { + qCWarning(KWAYLAND_SERVER) << "Invalid overscan requested:" << overscan; + return; + } + OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); + pendingChanges(output)->d->overscan = overscan; +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_set_vrr_policy(Resource *resource, wl_resource *outputdevice, uint32_t policy) +{ + Q_UNUSED(resource) + if (policy > static_cast(OutputDeviceV2Interface::VrrPolicy::Automatic)) { + qCWarning(KWAYLAND_SERVER) << "Invalid Vrr Policy requested:" << policy; + return; + } + OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice); + pendingChanges(output)->d->vrrPolicy = static_cast(policy); +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void OutputConfigurationV2InterfacePrivate::kde_output_configuration_v2_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource) + delete q; +} + +void OutputConfigurationV2InterfacePrivate::emitConfigurationChangeRequested() const +{ + auto configinterface = reinterpret_cast(q); + Q_EMIT outputManagement->configurationChangeRequested(configinterface); +} + +OutputConfigurationV2InterfacePrivate::OutputConfigurationV2InterfacePrivate(OutputConfigurationV2Interface *q, OutputManagementV2Interface *outputManagement, wl_resource *resource) + : QtWaylandServer::kde_output_configuration_v2(resource) + , outputManagement(outputManagement) + , q(q) +{ +} + +QHash OutputConfigurationV2Interface::changes() const +{ + return d->changes; +} + +void OutputConfigurationV2Interface::setApplied() +{ + d->clearPendingChanges(); + d->sendApplied(); +} + +void OutputConfigurationV2InterfacePrivate::sendApplied() +{ + send_applied(); +} + +void OutputConfigurationV2Interface::setFailed() +{ + d->clearPendingChanges(); + d->sendFailed(); +} + +void OutputConfigurationV2InterfacePrivate::sendFailed() +{ + send_failed(); +} + +OutputChangeSetV2 *OutputConfigurationV2InterfacePrivate::pendingChanges(OutputDeviceV2Interface *outputdevice) +{ + auto &change = changes[outputdevice]; + if (!change) { + change = new OutputChangeSetV2(outputdevice, q); + } + return change; +} + +bool OutputConfigurationV2InterfacePrivate::hasPendingChanges(OutputDeviceV2Interface *outputdevice) const +{ + auto it = changes.constFind(outputdevice); + if (it == changes.constEnd()) { + return false; + } + auto c = *it; + return c->enabledChanged() || + c->sizeChanged() || + c->refreshRateChanged() || + c->transformChanged() || + c->positionChanged() || + c->scaleChanged(); +} + +void OutputConfigurationV2InterfacePrivate::clearPendingChanges() +{ + qDeleteAll(changes.begin(), changes.end()); + changes.clear(); +} + +OutputConfigurationV2Interface::OutputConfigurationV2Interface(OutputManagementV2Interface *parent, wl_resource *resource) + : QObject() + , d(new OutputConfigurationV2InterfacePrivate(this, parent, resource)) +{ +} + +OutputConfigurationV2Interface::~OutputConfigurationV2Interface() +{ + d->clearPendingChanges(); +} + +} diff --git a/src/wayland/outputconfiguration_v2_interface.h b/src/wayland/outputconfiguration_v2_interface.h new file mode 100644 index 0000000000..45251b53be --- /dev/null +++ b/src/wayland/outputconfiguration_v2_interface.h @@ -0,0 +1,91 @@ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + SPDX-FileCopyrightText: 2021 Méven Car + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#pragma once + +#include "outputmanagement_v2_interface.h" +#include "outputdevice_v2_interface.h" +#include "outputchangeset_v2.h" + +#include + +namespace KWaylandServer +{ + +class OutputConfigurationV2InterfacePrivate; + +/** @class OutputConfigurationInterface + * + * Holds a new configuration for the outputs. + * + * The overall mechanism is to get a new OutputConfiguration from the OutputManagement global and + * apply changes through the OutputConfiguration::set* calls. When all changes are set, the client + * calls apply, which asks the server to look at the changes and apply them. The server will then + * signal back whether the changes have been applied successfully (@c setApplied()) or were rejected + * or failed to apply (@c setFailed()). + * + * Once the client has called applied, the OutputManagementInterface send the configuration object + * to the compositor through the OutputManagement::configurationChangeRequested(OutputConfiguration*) + * signal, the compositor can then decide what to do with the changes. + * + * These KWayland classes will not apply changes to the OutputDevices, this is the compositor's + * task. As such, the configuration set through this interface can be seen as a hint what the + * compositor should set up, but whether or not the compositor does it (based on hardware or + * rendering policies, for example), is up to the compositor. The mode setting is passed on to + * the DRM subsystem through the compositor. The compositor also saves this configuration and reads + * it on startup, this interface is not involved in that process. + * + * @see OutputManagementInterface + * @see OutputConfiguration + */ +class KWAYLANDSERVER_EXPORT OutputConfigurationV2Interface : public QObject +{ + Q_OBJECT +public: + ~OutputConfigurationV2Interface() override; + + /** + * Accessor for the changes made to OutputDevices. The data returned from this call + * will be deleted by the OutputConfigurationInterface when + * OutputManagementInterface::setApplied() or OutputManagementInterface::setFailed() + * is called, and on destruction of the OutputConfigurationInterface, so make sure you + * do not keep these pointers around. + * @returns A QHash of ChangeSets per outputdevice. + * @see ChangeSet + * @see OutputDeviceInterface + * @see OutputManagement + */ + QHash changes() const; + +public Q_SLOTS: + /** + * Called by the compositor once the changes have successfully been applied. + * The compositor is responsible for updating the OutputDevices. After having + * done so, calling this function sends applied() through the client. + * @see setFailed + * @see OutputConfiguration::applied + */ + void setApplied(); + /** + * Called by the compositor when the changes as a whole are rejected or + * failed to apply. This function results in the client OutputConfiguration emitting + * failed(). + * @see setApplied + * @see OutputConfiguration::failed + */ + void setFailed(); + +private: + explicit OutputConfigurationV2Interface(OutputManagementV2Interface *parent, wl_resource *resource); + friend class OutputManagementV2InterfacePrivate; + + QScopedPointer d; +}; + + +} + +Q_DECLARE_METATYPE(KWaylandServer::OutputConfigurationV2Interface*) diff --git a/src/wayland/outputdevice_v2_interface.cpp b/src/wayland/outputdevice_v2_interface.cpp new file mode 100644 index 0000000000..12222cbc6c --- /dev/null +++ b/src/wayland/outputdevice_v2_interface.cpp @@ -0,0 +1,773 @@ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2021 Méven Car + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#include "outputdevice_v2_interface.h" + +#include "display_p.h" +#include "display.h" +#include "logging.h" +#include "utils.h" + +#include +#include +#include + +#include "qwayland-server-kde-output-device-v2.h" + +namespace KWaylandServer +{ + +static const quint32 s_version = 1; + +class OutputDeviceV2InterfacePrivate : public QtWaylandServer::kde_output_device_v2 +{ +public: + OutputDeviceV2InterfacePrivate(OutputDeviceV2Interface *q, Display *display); + ~OutputDeviceV2InterfacePrivate() override; + + void updateGeometry(); + void updateUuid(); + void updateEdid(); + void updateEnabled(); + void updateScale(); + void updateEisaId(); + void updateSerialNumber(); + void updateCapabilities(); + void updateOverscan(); + void updateVrrPolicy(); + + void sendGeometry(Resource *resource); + wl_resource *sendNewMode(Resource *resource, OutputDeviceModeV2Interface *mode); + void sendCurrentMode(Resource *resource, OutputDeviceModeV2Interface *mode); + void sendDone(Resource *resource); + void sendUuid(Resource *resource); + void sendEdid(Resource *resource); + void sendEnabled(Resource *resource); + void sendScale(Resource *resource); + void sendEisaId(Resource *resource); + void sendSerialNumber(Resource *resource); + void sendCapabilities(Resource *resource); + void sendOverscan(Resource *resource); + void sendVrrPolicy(Resource *resource); + + QSize physicalSize; + QPoint globalPosition; + QString manufacturer = QStringLiteral("org.kde.kwin"); + QString model = QStringLiteral("none"); + qreal scale = 1.0; + QString serialNumber; + QString eisaId; + OutputDeviceV2Interface::SubPixel subPixel = OutputDeviceV2Interface::SubPixel::Unknown; + OutputDeviceV2Interface::Transform transform = OutputDeviceV2Interface::Transform::Normal; + + QList modes; + OutputDeviceModeV2Interface *currentMode = nullptr; + + QByteArray edid; + bool enabled = true; + QUuid uuid; + OutputDeviceV2Interface::Capabilities capabilities; + uint32_t overscan = 0; + OutputDeviceV2Interface::VrrPolicy vrrPolicy = OutputDeviceV2Interface::VrrPolicy::Automatic; + + QPointer display; + OutputDeviceV2Interface *q; + +private: + int32_t toTransform() const; + int32_t toSubPixel() const; + +protected: + void kde_output_device_v2_bind_resource(Resource *resource) override; + void kde_output_device_v2_destroy_global() override; +}; + +class OutputDeviceModeV2InterfacePrivate : public QtWaylandServer::kde_output_device_mode_v2 +{ +public: + OutputDeviceModeV2InterfacePrivate(OutputDeviceModeV2Interface *q, const QSize &size, int refreshRate, OutputDeviceModeV2Interface::ModeFlags flags); + ~OutputDeviceModeV2InterfacePrivate() override; + + void bindResource(wl_resource *resource); + + static OutputDeviceModeV2InterfacePrivate *get(OutputDeviceModeV2Interface *mode) { return mode->d.data(); } + + OutputDeviceModeV2Interface *q; + + QSize m_size; + int m_refreshRate = 60000; + OutputDeviceModeV2Interface::ModeFlags m_flags; +}; + +OutputDeviceV2InterfacePrivate::OutputDeviceV2InterfacePrivate(OutputDeviceV2Interface *q, Display *display) + : QtWaylandServer::kde_output_device_v2(*display, s_version) + , display(display) + , q(q) +{ + DisplayPrivate *displayPrivate = DisplayPrivate::get(display); + displayPrivate->outputdevicesV2.append(q); +} + +OutputDeviceV2InterfacePrivate::~OutputDeviceV2InterfacePrivate() +{ + if (display) { + DisplayPrivate *displayPrivate = DisplayPrivate::get(display); + displayPrivate->outputdevicesV2.removeOne(q); + } +} + +OutputDeviceV2Interface::OutputDeviceV2Interface(Display *display, QObject *parent) + : QObject(parent) + , d(new OutputDeviceV2InterfacePrivate(this, display)) +{ + connect(this, &OutputDeviceV2Interface::subPixelChanged, this, [this] { d->updateGeometry(); }); + connect(this, &OutputDeviceV2Interface::transformChanged, this, [this] { d->updateGeometry(); }); + connect(this, &OutputDeviceV2Interface::globalPositionChanged, this, [this] { d->updateGeometry(); }); + connect(this, &OutputDeviceV2Interface::modelChanged, this, [this] { d->updateGeometry(); }); + connect(this, &OutputDeviceV2Interface::manufacturerChanged, this, [this] { d->updateGeometry(); }); + connect(this, &OutputDeviceV2Interface::scaleChanged, this, [this] { d->updateScale(); }); +} + +OutputDeviceV2Interface::~OutputDeviceV2Interface() +{ + d->globalRemove(); +} + +void OutputDeviceV2Interface::remove() +{ + if (d->isGlobalRemoved()) { + return; + } + + if (d->display) { + DisplayPrivate *displayPrivate = DisplayPrivate::get(d->display); + displayPrivate->outputdevicesV2.removeOne(this); + } + + d->globalRemove(); +} + +QSize OutputDeviceV2Interface::pixelSize() const +{ + if (d->currentMode == nullptr) { + return QSize(); + } + return d->currentMode->size(); +} + +int OutputDeviceV2Interface::refreshRate() const +{ + if (d->currentMode == nullptr) { + return 60000; + } + return d->currentMode->refreshRate(); +} + +void OutputDeviceV2Interface::setCurrentMode(OutputDeviceModeV2Interface *mode) +{ + if (mode == d->currentMode) { + return; + } + if (d->currentMode) { + // another mode has the current flag - remove + d->currentMode->setFlags(d->currentMode->flags() & ~uint(OutputDeviceModeV2Interface::ModeFlag::Current)); + } + + mode->setFlags(mode->flags() | OutputDeviceModeV2Interface::ModeFlag::Current); + d->currentMode = mode; + + const auto clientResources = d->resourceMap(); + for (auto it = clientResources.begin(); it != clientResources.end(); ++it) { + auto resource = *it; + d->sendCurrentMode(resource, d->currentMode); + d->sendDone(resource); + } + + Q_EMIT currentModeChanged(); +} + +bool OutputDeviceV2Interface::setCurrentMode(const QSize &size, int refreshRate) +{ + auto mode = std::find_if(d->modes.begin(), d->modes.end(), + [size, refreshRate](OutputDeviceModeV2Interface *mode) { + return mode->size() == size && mode->refreshRate() == refreshRate; + } + ); + if (mode == d->modes.end()) { + return false; + } + setCurrentMode(*mode); + return true; +} + +int32_t OutputDeviceV2InterfacePrivate::toTransform() const +{ + switch (transform) { + case OutputDeviceV2Interface::Transform::Normal: + return WL_OUTPUT_TRANSFORM_NORMAL; + case OutputDeviceV2Interface::Transform::Rotated90: + return WL_OUTPUT_TRANSFORM_90; + case OutputDeviceV2Interface::Transform::Rotated180: + return WL_OUTPUT_TRANSFORM_180; + case OutputDeviceV2Interface::Transform::Rotated270: + return WL_OUTPUT_TRANSFORM_270; + case OutputDeviceV2Interface::Transform::Flipped: + return WL_OUTPUT_TRANSFORM_FLIPPED; + case OutputDeviceV2Interface::Transform::Flipped90: + return WL_OUTPUT_TRANSFORM_FLIPPED_90; + case OutputDeviceV2Interface::Transform::Flipped180: + return WL_OUTPUT_TRANSFORM_FLIPPED_180; + case OutputDeviceV2Interface::Transform::Flipped270: + return WL_OUTPUT_TRANSFORM_FLIPPED_270; + default: + Q_UNREACHABLE(); + } +} + +int32_t OutputDeviceV2InterfacePrivate::toSubPixel() const +{ + switch (subPixel) { + case OutputDeviceV2Interface::SubPixel::Unknown: + return WL_OUTPUT_SUBPIXEL_UNKNOWN; + case OutputDeviceV2Interface::SubPixel::None: + return WL_OUTPUT_SUBPIXEL_NONE; + case OutputDeviceV2Interface::SubPixel::HorizontalRGB: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; + case OutputDeviceV2Interface::SubPixel::HorizontalBGR: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; + case OutputDeviceV2Interface::SubPixel::VerticalRGB: + return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; + case OutputDeviceV2Interface::SubPixel::VerticalBGR: + return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; + default: + Q_UNREACHABLE(); + } +} + +void OutputDeviceV2InterfacePrivate::kde_output_device_v2_destroy_global() +{ + delete q; +} + +void OutputDeviceV2InterfacePrivate::kde_output_device_v2_bind_resource(Resource *resource) +{ + sendGeometry(resource); + sendScale(resource); + sendEisaId(resource); + sendSerialNumber(resource); + + auto currentModeIt = modes.end(); + for (auto it = modes.begin(); it != modes.end(); ++it) { + auto &mode = *it; + if (mode->flags().testFlag(OutputDeviceModeV2Interface::ModeFlag::Current)) { + // needs to be sent as last mode + currentModeIt = it; + continue; + } + sendNewMode(resource, mode); + } + + if (currentModeIt != modes.end()) { + auto modeResource = sendNewMode(resource, *currentModeIt); + send_current_mode(resource->handle, modeResource); + } + + sendUuid(resource); + sendEdid(resource); + sendEnabled(resource); + sendCapabilities(resource); + sendOverscan(resource); + sendVrrPolicy(resource); + sendDone(resource); +} + +wl_resource *OutputDeviceV2InterfacePrivate::sendNewMode(Resource *resource, OutputDeviceModeV2Interface *mode) +{ + auto privateMode = OutputDeviceModeV2InterfacePrivate::get(mode); + // bind mode to client + const auto modeResource = privateMode->add(resource->client(), resource->version())->handle; + + send_mode(resource->handle, modeResource); + + privateMode->bindResource(modeResource); + + return modeResource; +} + +void OutputDeviceV2InterfacePrivate::sendCurrentMode(Resource *outputResource, OutputDeviceModeV2Interface *mode) +{ + // mode must already be known to the client + const auto modeResources = OutputDeviceModeV2InterfacePrivate::get(mode)->resourceMap(); + for (auto modeResource : modeResources) { + send_current_mode(outputResource->handle, modeResource->handle); + } +} + +void OutputDeviceV2InterfacePrivate::sendGeometry(Resource *resource) +{ + send_geometry(resource->handle, + globalPosition.x(), + globalPosition.y(), + physicalSize.width(), + physicalSize.height(), + toSubPixel(), + manufacturer, + model, + toTransform()); +} + +void OutputDeviceV2InterfacePrivate::sendScale(Resource *resource) +{ + send_scale(resource->handle, wl_fixed_from_double(scale)); +} + +void OutputDeviceV2InterfacePrivate::sendSerialNumber(Resource *resource) +{ + send_serial_number(resource->handle, serialNumber); +} + +void OutputDeviceV2InterfacePrivate::sendEisaId(Resource *resource) +{ + send_eisa_id(resource->handle, eisaId); +} + +void OutputDeviceV2InterfacePrivate::sendDone(Resource *resource) +{ + send_done(resource->handle); +} + +void OutputDeviceV2InterfacePrivate::updateGeometry() +{ + const auto clientResources = resourceMap(); + for (auto resource : clientResources) { + sendGeometry(resource); + sendDone(resource); + } +} + +void OutputDeviceV2InterfacePrivate::updateScale() +{ + const auto clientResources = resourceMap(); + for (auto resource : clientResources) { + sendScale(resource); + sendDone(resource); + } +} + +void OutputDeviceV2Interface::setPhysicalSize(const QSize &arg) +{ + if (d->physicalSize == arg) { + return; + } + d->physicalSize = arg; + Q_EMIT physicalSizeChanged(d->physicalSize); +} + +void OutputDeviceV2Interface::setGlobalPosition(const QPoint &arg) +{ + if (d->globalPosition == arg) { + return; + } + d->globalPosition = arg; + Q_EMIT globalPositionChanged(d->globalPosition); +} + +void OutputDeviceV2Interface::setManufacturer(const QString &arg) +{ + if (d->manufacturer == arg) { + return; + } + d->manufacturer = arg; + Q_EMIT manufacturerChanged(d->manufacturer); +} + +void OutputDeviceV2Interface::setModel(const QString &arg) +{ + if (d->model == arg) { + return; + } + d->model = arg; + Q_EMIT modelChanged(d->model); +} + +void OutputDeviceV2Interface::setSerialNumber(const QString &arg) +{ + if (d->serialNumber == arg) { + return; + } + d->serialNumber = arg; + Q_EMIT serialNumberChanged(d->serialNumber); +} + +void OutputDeviceV2Interface::setEisaId(const QString &arg) +{ + if (d->eisaId == arg) { + return; + } + d->eisaId = arg; + Q_EMIT eisaIdChanged(d->eisaId); +} + +void OutputDeviceV2Interface::setSubPixel(SubPixel arg) +{ + if (d->subPixel == arg) { + return; + } + d->subPixel = arg; + Q_EMIT subPixelChanged(d->subPixel); +} + +void OutputDeviceV2Interface::setTransform(Transform arg) +{ + if (d->transform == arg) { + return; + } + d->transform = arg; + Q_EMIT transformChanged(d->transform); +} + +void OutputDeviceV2Interface::setScale(qreal scale) +{ + if (qFuzzyCompare(d->scale, scale)) { + return; + } + d->scale = scale; + Q_EMIT scaleChanged(d->scale); +} + +QSize OutputDeviceV2Interface::physicalSize() const +{ + return d->physicalSize; +} + +QPoint OutputDeviceV2Interface::globalPosition() const +{ + return d->globalPosition; +} + +QString OutputDeviceV2Interface::manufacturer() const +{ + return d->manufacturer; +} + +QString OutputDeviceV2Interface::model() const +{ + return d->model; +} + +QString OutputDeviceV2Interface::serialNumber() const +{ + return d->serialNumber; +} + +QString OutputDeviceV2Interface::eisaId() const +{ + return d->eisaId; +} + +qreal OutputDeviceV2Interface::scale() const +{ + return d->scale; +} + +OutputDeviceV2Interface::SubPixel OutputDeviceV2Interface::subPixel() const +{ + return d->subPixel; +} + +OutputDeviceV2Interface::Transform OutputDeviceV2Interface::transform() const +{ + return d->transform; +} + +void OutputDeviceV2Interface::setModes(const QList &modes) +{ + if (modes.isEmpty()) { + qCWarning(KWAYLAND_SERVER) << "Tried to set no modes for output"; + return; + } + + const auto clientResources = d->resourceMap(); + + const auto oldModes = d->modes; + d->modes.clear(); + + const auto oldCurrentMode = d->currentMode; + d->currentMode = nullptr; + + for (OutputDeviceModeV2Interface *outputDeviceMode : modes) { + d->modes << outputDeviceMode; + outputDeviceMode->setParent(this); + + if (outputDeviceMode->flags().testFlag(OutputDeviceModeV2Interface::ModeFlag::Current)) { + d->currentMode = outputDeviceMode; + } else { + for (auto resource : clientResources) { + d->sendNewMode(resource, outputDeviceMode); + } + } + } + + if (!d->currentMode) { + d->currentMode = d->modes.at(0); + } + + for (auto resource : clientResources) { + d->sendNewMode(resource, d->currentMode); + d->sendCurrentMode(resource, d->currentMode); + } + + qDeleteAll(oldModes.crbegin(), oldModes.crend()); + + for (auto resource : clientResources) { + d->sendDone(resource); + } + + if (oldCurrentMode != d->currentMode) { + Q_EMIT currentModeChanged(); + } + + Q_EMIT modesChanged(); +} + +void OutputDeviceV2Interface::setEdid(const QByteArray &edid) +{ + d->edid = edid; + d->updateEdid(); + Q_EMIT edidChanged(); +} + +QByteArray OutputDeviceV2Interface::edid() const +{ + return d->edid; +} + +void OutputDeviceV2Interface::setEnabled(bool enabled) +{ + if (d->enabled != enabled) { + d->enabled = enabled; + d->updateEnabled(); + Q_EMIT enabledChanged(); + } +} + +bool OutputDeviceV2Interface::enabled() const +{ + return d->enabled; +} + +void OutputDeviceV2Interface::setUuid(const QUuid &uuid) +{ + if (d->uuid != uuid) { + d->uuid = uuid; + d->updateUuid(); + Q_EMIT uuidChanged(); + } +} + +QUuid OutputDeviceV2Interface::uuid() const +{ + return d->uuid; +} + +void OutputDeviceV2InterfacePrivate::sendEdid(Resource *resource) +{ + send_edid(resource->handle, QString::fromStdString(edid.toBase64().toStdString())); +} + +void OutputDeviceV2InterfacePrivate::sendEnabled(Resource *resource) +{ + send_enabled(resource->handle, enabled); +} + +void OutputDeviceV2InterfacePrivate::sendUuid(Resource *resource) +{ + send_uuid(resource->handle, uuid.toString(QUuid::WithoutBraces)); +} + +void OutputDeviceV2InterfacePrivate::updateEnabled() +{ + const auto clientResources = resourceMap(); + for (auto resource : clientResources) { + sendEnabled(resource); + } +} + +void OutputDeviceV2InterfacePrivate::updateEdid() +{ + const auto clientResources = resourceMap(); + for (auto resource : clientResources) { + sendEdid(resource); + } +} + +void OutputDeviceV2InterfacePrivate::updateUuid() +{ + const auto clientResources = resourceMap(); + for (auto resource : clientResources) { + sendUuid(resource); + } +} + +void OutputDeviceV2InterfacePrivate::updateEisaId() +{ + const auto clientResources = resourceMap(); + for (auto resource : clientResources) { + sendEisaId(resource); + } +} + +uint32_t OutputDeviceV2Interface::overscan() const +{ + return d->overscan; +} + +OutputDeviceV2Interface::Capabilities OutputDeviceV2Interface::capabilities() const +{ + return d->capabilities; +} + +void OutputDeviceV2Interface::setCapabilities(Capabilities cap) +{ + if (d->capabilities != cap) { + d->capabilities = cap; + d->updateCapabilities(); + Q_EMIT capabilitiesChanged(); + } +} + +void OutputDeviceV2InterfacePrivate::sendCapabilities(Resource *resource) +{ + send_capabilities(resource->handle, static_cast(capabilities)); +} + +void OutputDeviceV2InterfacePrivate::updateCapabilities() +{ + const auto clientResources = resourceMap(); + for (const auto &resource : clientResources) { + sendCapabilities(resource); + } +} + +void OutputDeviceV2Interface::setOverscan(uint32_t overscan) +{ + if (d->overscan != overscan) { + d->overscan = overscan; + d->updateOverscan(); + Q_EMIT overscanChanged(); + } +} + +void OutputDeviceV2InterfacePrivate::sendOverscan(Resource *resource) +{ + send_overscan(resource->handle, static_cast(overscan)); +} + +void OutputDeviceV2InterfacePrivate::updateOverscan() +{ + const auto clientResources = resourceMap(); + for (const auto &resource : clientResources) { + sendOverscan(resource); + } +} + +void OutputDeviceV2InterfacePrivate::sendVrrPolicy(Resource *resource) +{ + send_vrr_policy(resource->handle, static_cast(vrrPolicy)); +} + +OutputDeviceV2Interface::VrrPolicy OutputDeviceV2Interface::vrrPolicy() const +{ + return d->vrrPolicy; +} + +void OutputDeviceV2Interface::setVrrPolicy(VrrPolicy policy) +{ + if (d->vrrPolicy != policy) { + d->vrrPolicy = policy; + d->updateVrrPolicy(); + Q_EMIT vrrPolicyChanged(); + } +} + +void OutputDeviceV2InterfacePrivate::updateVrrPolicy() +{ + const auto clientResources = resourceMap(); + for (const auto &resource : clientResources) { + sendVrrPolicy(resource); + } +} + +OutputDeviceV2Interface *OutputDeviceV2Interface::get(wl_resource *native) +{ + if (auto devicePrivate = resource_cast(native)) { + return devicePrivate->q; + } + return nullptr; +} + +OutputDeviceModeV2InterfacePrivate::OutputDeviceModeV2InterfacePrivate(OutputDeviceModeV2Interface *q, const QSize &size, int refreshRate, OutputDeviceModeV2Interface::ModeFlags flags) + : QtWaylandServer::kde_output_device_mode_v2() + , q(q) + , m_size(size) + , m_refreshRate(refreshRate) + , m_flags(flags) +{} + +OutputDeviceModeV2Interface::OutputDeviceModeV2Interface(const QSize &size, int refreshRate, ModeFlags flags, QObject *parent) + : QObject(parent) + , d(new OutputDeviceModeV2InterfacePrivate(this, size, refreshRate, flags)) +{} + +OutputDeviceModeV2Interface::~OutputDeviceModeV2Interface() = default; + +OutputDeviceModeV2InterfacePrivate::~OutputDeviceModeV2InterfacePrivate() +{ + const auto map = resourceMap(); + for (Resource *resource : map) { + send_removed(resource->handle); + } +} + +QSize OutputDeviceModeV2Interface::size() const +{ + return d->m_size; +} + +int OutputDeviceModeV2Interface::refreshRate() const +{ + return d->m_refreshRate; +} + +OutputDeviceModeV2Interface::ModeFlags OutputDeviceModeV2Interface::flags() const +{ + return d->m_flags; +} + +void OutputDeviceModeV2Interface::setFlags(OutputDeviceModeV2Interface::ModeFlags flags) +{ + d->m_flags = flags; +} + +void OutputDeviceModeV2InterfacePrivate::bindResource(wl_resource *resource) +{ + send_size(resource, m_size.width(), m_size.height()); + send_refresh(resource, m_refreshRate); + + if (m_flags.testFlag(OutputDeviceModeV2Interface::ModeFlag::Preferred)) { + send_preferred(resource); + } +} + +OutputDeviceModeV2Interface *OutputDeviceModeV2Interface::get(wl_resource *native) +{ + if (auto devicePrivate = resource_cast(native)) { + return devicePrivate->q; + } + return nullptr; +} + +} diff --git a/src/wayland/outputdevice_v2_interface.h b/src/wayland/outputdevice_v2_interface.h new file mode 100644 index 0000000000..8996ce85a0 --- /dev/null +++ b/src/wayland/outputdevice_v2_interface.h @@ -0,0 +1,204 @@ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2021 Méven Car + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#pragma once + +#include + +#include +#include +#include +#include +#include + +struct wl_resource; + +namespace KWaylandServer +{ + +class Display; +class OutputDeviceV2InterfacePrivate; +class OutputDeviceModeV2Interface; +class OutputDeviceModeV2InterfacePrivate; + + +/** @class OutputDeviceV2Interface + * + * Represents an output device, the difference to Output is that this output can be disabled, + * so not currently used to display content. + * + * @see OutputManagementV2Interface + */ +class KWAYLANDSERVER_EXPORT OutputDeviceV2Interface : public QObject +{ + 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(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) + Q_PROPERTY(QByteArray edid READ edid WRITE setEdid NOTIFY edidChanged) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QUuid uuid READ uuid WRITE setUuid NOTIFY uuidChanged) + Q_PROPERTY(Capabilities capabilities READ capabilities WRITE setCapabilities NOTIFY capabilitiesChanged) + Q_PROPERTY(uint32_t overscan READ overscan WRITE setOverscan NOTIFY overscanChanged) + Q_PROPERTY(VrrPolicy vrrPolicy READ vrrPolicy WRITE setVrrPolicy NOTIFY vrrPolicyChanged) +public: + enum class SubPixel { + Unknown, + None, + HorizontalRGB, + HorizontalBGR, + VerticalRGB, + VerticalBGR, + }; + Q_ENUM(SubPixel) + enum class Transform { + Normal, + Rotated90, + Rotated180, + Rotated270, + Flipped, + Flipped90, + Flipped180, + Flipped270, + }; + Q_ENUM(Transform) + enum class Capability { + Overscan = 0x1, + Vrr = 0x2, + }; + Q_ENUM(Capability) + Q_DECLARE_FLAGS(Capabilities, Capability) + enum class VrrPolicy { + Never = 0, + Always = 1, + Automatic = 2 + }; + Q_ENUM(VrrPolicy) + + explicit OutputDeviceV2Interface(Display *display, QObject *parent = nullptr); + ~OutputDeviceV2Interface() override; + + void remove(); + + QSize physicalSize() const; + QPoint globalPosition() const; + QString manufacturer() const; + QString model() const; + QString serialNumber() const; + QString eisaId() const; + QSize pixelSize() const; + int refreshRate() const; + + qreal scale() const; + SubPixel subPixel() const; + Transform transform() const; + + QByteArray edid() const; + bool enabled() const; + QUuid uuid() const; + + Capabilities capabilities() const; + uint32_t overscan() const; + VrrPolicy vrrPolicy() 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(qreal scale); + void setSubPixel(SubPixel subPixel); + void setTransform(Transform transform); + + void setModes(const QList &modes); + void setCurrentMode(KWaylandServer::OutputDeviceModeV2Interface *mode); + + /** + * Makes the mode with the specified @a size and @a refreshRate current. + * Returns @c false if no mode with the given attributes exists; otherwise returns @c true. + */ + bool setCurrentMode(const QSize &size, int refreshRate); + + void setEdid(const QByteArray &edid); + void setEnabled(bool enabled); + void setUuid(const QUuid &uuid); + + void setCapabilities(Capabilities cap); + void setOverscan(uint32_t overscan); + void setVrrPolicy(VrrPolicy policy); + + static OutputDeviceV2Interface *get(wl_resource *native); + +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 scaleChanged(qreal); + void subPixelChanged(SubPixel); + void transformChanged(Transform); + void modesChanged(); + void currentModeChanged(); + + void edidChanged(); + void enabledChanged(); + void uuidChanged(); + + void capabilitiesChanged(); + void overscanChanged(); + void vrrPolicyChanged(); + +private: + QScopedPointer d; +}; + +/** + * @class The OutputDeviceModeV2Interface class + * + * Represents an output device mode. + * +* @see OutputDeviceV2Interface + */ +class KWAYLANDSERVER_EXPORT OutputDeviceModeV2Interface : public QObject +{ + Q_OBJECT +public: + enum class ModeFlag { + Current = 0x1, + Preferred = 0x2, + }; + Q_ENUM(ModeFlag) + Q_DECLARE_FLAGS(ModeFlags, ModeFlag) + + OutputDeviceModeV2Interface(const QSize &size, int refreshRate, ModeFlags flags, QObject *parent = nullptr); + ~OutputDeviceModeV2Interface() override; + + QSize size() const; + int refreshRate() const; + OutputDeviceModeV2Interface::ModeFlags flags() const; + + void setFlags(OutputDeviceModeV2Interface::ModeFlags newFlags); + + static OutputDeviceModeV2Interface *get(wl_resource *native); + +private: + friend class OutputDeviceModeV2InterfacePrivate; + QScopedPointer d; +}; + +} + +Q_DECLARE_METATYPE(KWaylandServer::OutputDeviceModeV2Interface::ModeFlag) +Q_DECLARE_METATYPE(KWaylandServer::OutputDeviceV2Interface::SubPixel) +Q_DECLARE_METATYPE(KWaylandServer::OutputDeviceV2Interface::Transform) diff --git a/src/wayland/outputmanagement_v2_interface.cpp b/src/wayland/outputmanagement_v2_interface.cpp new file mode 100644 index 0000000000..92c2294e99 --- /dev/null +++ b/src/wayland/outputmanagement_v2_interface.cpp @@ -0,0 +1,57 @@ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#include "display.h" +#include "outputmanagement_v2_interface.h" +#include "outputconfiguration_v2_interface.h" + +#include +#include "qwayland-server-kde-output-management-v2.h" + +#include + +namespace KWaylandServer +{ + +static const quint32 s_version = 1; + +class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2 +{ +public: + OutputManagementV2InterfacePrivate(OutputManagementV2Interface *_q, Display *display); + +private: + OutputManagementV2Interface *q; + +protected: + void kde_output_management_v2_create_configuration(Resource *resource, uint32_t id) override; +}; + +OutputManagementV2InterfacePrivate::OutputManagementV2InterfacePrivate(OutputManagementV2Interface *_q, Display *display) + : QtWaylandServer::kde_output_management_v2(*display, s_version) + , q(_q) +{ +} + +void OutputManagementV2InterfacePrivate::kde_output_management_v2_create_configuration(Resource *resource, uint32_t id) +{ + wl_resource *config_resource = wl_resource_create(resource->client(), &kde_output_configuration_v2_interface, resource->version(), id); + if (!config_resource) { + wl_client_post_no_memory(resource->client()); + return; + } + new OutputConfigurationV2Interface(q, config_resource); +} + +OutputManagementV2Interface::OutputManagementV2Interface(Display *display, QObject *parent) + : QObject(parent) + , d(new OutputManagementV2InterfacePrivate(this, display)) +{ +} + +OutputManagementV2Interface::~OutputManagementV2Interface() = default; + +} diff --git a/src/wayland/outputmanagement_v2_interface.h b/src/wayland/outputmanagement_v2_interface.h new file mode 100644 index 0000000000..5257f965f7 --- /dev/null +++ b/src/wayland/outputmanagement_v2_interface.h @@ -0,0 +1,58 @@ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#pragma once + +#include + +#include + +namespace KWaylandServer +{ + +class OutputManagementV2InterfacePrivate; +class OutputConfigurationV2Interface; +/** + * @class OutputManagementInterface + * + * This class is used to change the configuration of the Wayland server's outputs. + * The client requests an OutputConfiguration, changes its OutputDevices and then + * calls OutputConfiguration::apply, which makes this class emit a signal, carrying + * the new configuration. + * The server is then expected to make the requested changes by applying the settings + * of the OutputDevices to the Outputs. + * + * @see OutputConfiguration + * @see OutputConfigurationInterface + */ +class KWAYLANDSERVER_EXPORT OutputManagementV2Interface : public QObject +{ + Q_OBJECT + +public: + explicit OutputManagementV2Interface(Display *display, QObject *parent = nullptr); + ~OutputManagementV2Interface() override; + +Q_SIGNALS: + /** + * Emitted after the client has requested an OutputConfiguration to be applied. + * through OutputConfiguration::apply. The compositor can use this object to get + * notified when the new configuration is set up, and it should be applied to the + * Wayland server's OutputInterfaces. + * + * @param config The OutputConfigurationInterface corresponding to the client that + * called apply(). + * @see OutputConfiguration::apply + * @see OutputConfigurationInterface + * @see OutputDeviceInterface + * @see OutputInterface + */ + void configurationChangeRequested(KWaylandServer::OutputConfigurationV2Interface *configurationInterface); + +private: + QScopedPointer d; +}; + +}