File indexing completed on 2024-05-19 16:35:22

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 #include "outputmanagement_v2_interface.h"
0008 #include "display.h"
0009 #include "outputdevice_v2_interface.h"
0010 #include "outputmanagement_v2_interface.h"
0011 #include "utils/common.h"
0012 
0013 #include "core/outputbackend.h"
0014 #include "core/outputconfiguration.h"
0015 #include "main.h"
0016 #include "workspace.h"
0017 
0018 #include "qwayland-server-kde-output-management-v2.h"
0019 
0020 #include <optional>
0021 
0022 using namespace KWin;
0023 
0024 namespace KWaylandServer
0025 {
0026 
0027 static const quint32 s_version = 3;
0028 
0029 class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2
0030 {
0031 public:
0032     OutputManagementV2InterfacePrivate(Display *display);
0033 
0034 protected:
0035     void kde_output_management_v2_create_configuration(Resource *resource, uint32_t id) override;
0036 };
0037 
0038 class OutputConfigurationV2Interface : public QObject, QtWaylandServer::kde_output_configuration_v2
0039 {
0040     Q_OBJECT
0041 public:
0042     explicit OutputConfigurationV2Interface(wl_resource *resource);
0043 
0044     bool applied = false;
0045     bool invalid = false;
0046     OutputConfiguration config;
0047     QVector<std::pair<uint32_t, OutputDeviceV2Interface *>> outputOrder;
0048 
0049 protected:
0050     void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override;
0051     void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override;
0052     void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override;
0053     void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override;
0054     void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override;
0055     void kde_output_configuration_v2_apply(Resource *resource) override;
0056     void kde_output_configuration_v2_destroy(Resource *resource) override;
0057     void kde_output_configuration_v2_destroy_resource(Resource *resource) override;
0058     void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override;
0059     void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override;
0060     void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override;
0061     void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override;
0062     void kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *output, uint32_t priority) override;
0063 };
0064 
0065 OutputManagementV2InterfacePrivate::OutputManagementV2InterfacePrivate(Display *display)
0066     : QtWaylandServer::kde_output_management_v2(*display, s_version)
0067 {
0068 }
0069 
0070 void OutputManagementV2InterfacePrivate::kde_output_management_v2_create_configuration(Resource *resource, uint32_t id)
0071 {
0072     wl_resource *config_resource = wl_resource_create(resource->client(), &kde_output_configuration_v2_interface, resource->version(), id);
0073     if (!config_resource) {
0074         wl_client_post_no_memory(resource->client());
0075         return;
0076     }
0077     new OutputConfigurationV2Interface(config_resource);
0078 }
0079 
0080 OutputManagementV2Interface::OutputManagementV2Interface(Display *display, QObject *parent)
0081     : QObject(parent)
0082     , d(new OutputManagementV2InterfacePrivate(display))
0083 {
0084 }
0085 
0086 OutputManagementV2Interface::~OutputManagementV2Interface() = default;
0087 
0088 OutputConfigurationV2Interface::OutputConfigurationV2Interface(wl_resource *resource)
0089     : QtWaylandServer::kde_output_configuration_v2(resource)
0090 {
0091     const auto reject = [this](Output *output) {
0092         invalid = true;
0093     };
0094     connect(workspace(), &Workspace::outputAdded, this, reject);
0095     connect(workspace(), &Workspace::outputRemoved, this, reject);
0096 }
0097 
0098 void OutputConfigurationV2Interface::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable)
0099 {
0100     if (invalid) {
0101         return;
0102     }
0103     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0104         config.changeSet(output->handle())->enabled = enable;
0105     }
0106 }
0107 
0108 void OutputConfigurationV2Interface::kde_output_configuration_v2_mode(Resource *resource, wl_resource *outputdevice, wl_resource *modeResource)
0109 {
0110     if (invalid) {
0111         return;
0112     }
0113     OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice);
0114     OutputDeviceModeV2Interface *mode = OutputDeviceModeV2Interface::get(modeResource);
0115     if (output && mode) {
0116         config.changeSet(output->handle())->mode = mode->handle().lock();
0117     } else {
0118         invalid = true;
0119     }
0120 }
0121 
0122 void OutputConfigurationV2Interface::kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform)
0123 {
0124     if (invalid) {
0125         return;
0126     }
0127     auto toTransform = [transform]() {
0128         switch (transform) {
0129         case WL_OUTPUT_TRANSFORM_90:
0130             return Output::Transform::Rotated90;
0131         case WL_OUTPUT_TRANSFORM_180:
0132             return Output::Transform::Rotated180;
0133         case WL_OUTPUT_TRANSFORM_270:
0134             return Output::Transform::Rotated270;
0135         case WL_OUTPUT_TRANSFORM_FLIPPED:
0136             return Output::Transform::Flipped;
0137         case WL_OUTPUT_TRANSFORM_FLIPPED_90:
0138             return Output::Transform::Flipped90;
0139         case WL_OUTPUT_TRANSFORM_FLIPPED_180:
0140             return Output::Transform::Flipped180;
0141         case WL_OUTPUT_TRANSFORM_FLIPPED_270:
0142             return Output::Transform::Flipped270;
0143         case WL_OUTPUT_TRANSFORM_NORMAL:
0144         default:
0145             return Output::Transform::Normal;
0146         }
0147     };
0148     auto _transform = toTransform();
0149     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0150         config.changeSet(output->handle())->transform = _transform;
0151     }
0152 }
0153 
0154 void OutputConfigurationV2Interface::kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y)
0155 {
0156     if (invalid) {
0157         return;
0158     }
0159     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0160         config.changeSet(output->handle())->pos = QPoint(x, y);
0161     }
0162 }
0163 
0164 void OutputConfigurationV2Interface::kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale)
0165 {
0166     if (invalid) {
0167         return;
0168     }
0169     qreal doubleScale = wl_fixed_to_double(scale);
0170 
0171     // the fractional scaling protocol only speaks in unit of 120ths
0172     // using the same scale throughout makes that simpler
0173     // this also eliminates most loss from wl_fixed
0174     doubleScale = std::round(doubleScale * 120) / 120;
0175 
0176     if (doubleScale <= 0) {
0177         qCWarning(KWIN_CORE) << "Requested to scale output device to" << doubleScale << ", but I can't do that.";
0178         return;
0179     }
0180 
0181     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0182         config.changeSet(output->handle())->scale = doubleScale;
0183     }
0184 }
0185 
0186 void OutputConfigurationV2Interface::kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan)
0187 {
0188     if (invalid) {
0189         return;
0190     }
0191     if (overscan > 100) {
0192         qCWarning(KWIN_CORE) << "Invalid overscan requested:" << overscan;
0193         return;
0194     }
0195     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0196         config.changeSet(output->handle())->overscan = overscan;
0197     }
0198 }
0199 
0200 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_vrr_policy(Resource *resource, wl_resource *outputdevice, uint32_t policy)
0201 {
0202     if (invalid) {
0203         return;
0204     }
0205     if (policy > static_cast<uint32_t>(RenderLoop::VrrPolicy::Automatic)) {
0206         qCWarning(KWIN_CORE) << "Invalid Vrr Policy requested:" << policy;
0207         return;
0208     }
0209     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0210         config.changeSet(output->handle())->vrrPolicy = static_cast<RenderLoop::VrrPolicy>(policy);
0211     }
0212 }
0213 
0214 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange)
0215 {
0216     if (invalid) {
0217         return;
0218     }
0219     if (rgbRange > static_cast<uint32_t>(Output::RgbRange::Limited)) {
0220         qCWarning(KWIN_CORE) << "Invalid Rgb Range requested:" << rgbRange;
0221         return;
0222     }
0223     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0224         config.changeSet(output->handle())->rgbRange = static_cast<Output::RgbRange>(rgbRange);
0225     }
0226 }
0227 
0228 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output)
0229 {
0230     // intentionally ignored
0231 }
0232 
0233 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *outputResource, uint32_t priority)
0234 {
0235     if (invalid) {
0236         return;
0237     }
0238     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputResource)) {
0239         outputOrder.push_back(std::make_pair(priority, output));
0240     }
0241 }
0242 
0243 void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy(Resource *resource)
0244 {
0245     wl_resource_destroy(resource->handle);
0246 }
0247 
0248 void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy_resource(Resource *resource)
0249 {
0250     delete this;
0251 }
0252 
0253 void OutputConfigurationV2Interface::kde_output_configuration_v2_apply(Resource *resource)
0254 {
0255     if (applied) {
0256         wl_resource_post_error(resource->handle, error_already_applied, "an output configuration can be applied only once");
0257         return;
0258     }
0259 
0260     applied = true;
0261     if (invalid) {
0262         qCWarning(KWIN_CORE) << "Rejecting configuration change because a request output is no longer available";
0263         send_failed();
0264         return;
0265     }
0266 
0267     const auto allOutputs = kwinApp()->outputBackend()->outputs();
0268     const bool allDisabled = !std::any_of(allOutputs.begin(), allOutputs.end(), [this](const auto &output) {
0269         return config.constChangeSet(output)->enabled;
0270     });
0271     if (allDisabled) {
0272         qCWarning(KWIN_CORE) << "Disabling all outputs through configuration changes is not allowed";
0273         send_failed();
0274         return;
0275     }
0276 
0277     QVector<Output *> sortedOrder;
0278     if (!outputOrder.empty()) {
0279         const int desktopOutputs = std::count_if(allOutputs.begin(), allOutputs.end(), [](Output *output) {
0280             return !output->isNonDesktop();
0281         });
0282         if (outputOrder.size() != desktopOutputs) {
0283             qWarning(KWIN_CORE) << "Provided output order doesn't contain all outputs!";
0284             send_failed();
0285             return;
0286         }
0287         outputOrder.erase(std::remove_if(outputOrder.begin(), outputOrder.end(), [this](const auto &pair) {
0288                               return !config.constChangeSet(pair.second->handle())->enabled;
0289                           }),
0290                           outputOrder.end());
0291         std::sort(outputOrder.begin(), outputOrder.end(), [](const auto &pair1, const auto &pair2) {
0292             return pair1.first < pair2.first;
0293         });
0294         uint32_t i = 1;
0295         for (const auto &[index, name] : std::as_const(outputOrder)) {
0296             if (index != i) {
0297                 qCWarning(KWIN_CORE) << "Provided output order is invalid!";
0298                 send_failed();
0299                 return;
0300             }
0301             i++;
0302         }
0303         sortedOrder.reserve(outputOrder.size());
0304         std::transform(outputOrder.begin(), outputOrder.end(), std::back_inserter(sortedOrder), [](const auto &pair) {
0305             return pair.second->handle();
0306         });
0307     }
0308     if (workspace()->applyOutputConfiguration(config, sortedOrder)) {
0309         send_applied();
0310     } else {
0311         qCDebug(KWIN_CORE) << "Applying config failed";
0312         send_failed();
0313     }
0314 }
0315 
0316 }
0317 #include "outputmanagement_v2_interface.moc"