File indexing completed on 2024-05-19 05:32:38

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.h"
0008 #include "core/iccprofile.h"
0009 #include "core/outputbackend.h"
0010 #include "core/outputconfiguration.h"
0011 #include "display.h"
0012 #include "main.h"
0013 #include "outputdevice_v2.h"
0014 #include "outputmanagement_v2.h"
0015 #include "utils/common.h"
0016 #include "workspace.h"
0017 
0018 #include "qwayland-server-kde-output-management-v2.h"
0019 
0020 #include <cmath>
0021 #include <optional>
0022 
0023 namespace KWin
0024 {
0025 
0026 static const quint32 s_version = 7;
0027 
0028 class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2
0029 {
0030 public:
0031     OutputManagementV2InterfacePrivate(Display *display);
0032 
0033 protected:
0034     void kde_output_management_v2_create_configuration(Resource *resource, uint32_t id) override;
0035 };
0036 
0037 class OutputConfigurationV2Interface : public QObject, QtWaylandServer::kde_output_configuration_v2
0038 {
0039     Q_OBJECT
0040 public:
0041     explicit OutputConfigurationV2Interface(wl_resource *resource);
0042 
0043     bool applied = false;
0044     bool invalid = false;
0045     OutputConfiguration config;
0046     QList<std::pair<uint32_t, OutputDeviceV2Interface *>> outputOrder;
0047 
0048 protected:
0049     void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override;
0050     void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override;
0051     void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override;
0052     void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override;
0053     void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override;
0054     void kde_output_configuration_v2_apply(Resource *resource) override;
0055     void kde_output_configuration_v2_destroy(Resource *resource) override;
0056     void kde_output_configuration_v2_destroy_resource(Resource *resource) override;
0057     void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override;
0058     void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override;
0059     void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override;
0060     void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override;
0061     void kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *output, uint32_t priority) override;
0062     void kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr) override;
0063     void kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness) override;
0064     void kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg) override;
0065     void kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy) override;
0066     void kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path) override;
0067     void kde_output_configuration_v2_set_brightness_overrides(Resource *resource, wl_resource *outputdevice, int32_t max_peak_brightness, int32_t max_average_brightness, int32_t min_brightness) override;
0068     void kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness) override;
0069 };
0070 
0071 OutputManagementV2InterfacePrivate::OutputManagementV2InterfacePrivate(Display *display)
0072     : QtWaylandServer::kde_output_management_v2(*display, s_version)
0073 {
0074 }
0075 
0076 void OutputManagementV2InterfacePrivate::kde_output_management_v2_create_configuration(Resource *resource, uint32_t id)
0077 {
0078     wl_resource *config_resource = wl_resource_create(resource->client(), &kde_output_configuration_v2_interface, resource->version(), id);
0079     if (!config_resource) {
0080         wl_client_post_no_memory(resource->client());
0081         return;
0082     }
0083     new OutputConfigurationV2Interface(config_resource);
0084 }
0085 
0086 OutputManagementV2Interface::OutputManagementV2Interface(Display *display, QObject *parent)
0087     : QObject(parent)
0088     , d(new OutputManagementV2InterfacePrivate(display))
0089 {
0090 }
0091 
0092 OutputManagementV2Interface::~OutputManagementV2Interface() = default;
0093 
0094 OutputConfigurationV2Interface::OutputConfigurationV2Interface(wl_resource *resource)
0095     : QtWaylandServer::kde_output_configuration_v2(resource)
0096 {
0097     const auto reject = [this](Output *output) {
0098         invalid = true;
0099     };
0100     connect(workspace(), &Workspace::outputAdded, this, reject);
0101     connect(workspace(), &Workspace::outputRemoved, this, reject);
0102 }
0103 
0104 void OutputConfigurationV2Interface::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable)
0105 {
0106     if (invalid) {
0107         return;
0108     }
0109     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0110         config.changeSet(output->handle())->enabled = enable;
0111     }
0112 }
0113 
0114 void OutputConfigurationV2Interface::kde_output_configuration_v2_mode(Resource *resource, wl_resource *outputdevice, wl_resource *modeResource)
0115 {
0116     if (invalid) {
0117         return;
0118     }
0119     OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice);
0120     OutputDeviceModeV2Interface *mode = OutputDeviceModeV2Interface::get(modeResource);
0121     if (output && mode) {
0122         config.changeSet(output->handle())->mode = mode->handle().lock();
0123     } else {
0124         invalid = true;
0125     }
0126 }
0127 
0128 void OutputConfigurationV2Interface::kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform)
0129 {
0130     if (invalid) {
0131         return;
0132     }
0133     auto toTransform = [transform]() {
0134         switch (transform) {
0135         case WL_OUTPUT_TRANSFORM_90:
0136             return OutputTransform::Rotate90;
0137         case WL_OUTPUT_TRANSFORM_180:
0138             return OutputTransform::Rotate180;
0139         case WL_OUTPUT_TRANSFORM_270:
0140             return OutputTransform::Rotate270;
0141         case WL_OUTPUT_TRANSFORM_FLIPPED:
0142             return OutputTransform::FlipX;
0143         case WL_OUTPUT_TRANSFORM_FLIPPED_90:
0144             return OutputTransform::FlipX90;
0145         case WL_OUTPUT_TRANSFORM_FLIPPED_180:
0146             return OutputTransform::FlipX180;
0147         case WL_OUTPUT_TRANSFORM_FLIPPED_270:
0148             return OutputTransform::FlipX270;
0149         case WL_OUTPUT_TRANSFORM_NORMAL:
0150         default:
0151             return OutputTransform::Normal;
0152         }
0153     };
0154     auto _transform = toTransform();
0155     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0156         const auto changeset = config.changeSet(output->handle());
0157         changeset->transform = changeset->manualTransform = _transform;
0158     }
0159 }
0160 
0161 void OutputConfigurationV2Interface::kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y)
0162 {
0163     if (invalid) {
0164         return;
0165     }
0166     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0167         config.changeSet(output->handle())->pos = QPoint(x, y);
0168     }
0169 }
0170 
0171 void OutputConfigurationV2Interface::kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale)
0172 {
0173     if (invalid) {
0174         return;
0175     }
0176     qreal doubleScale = wl_fixed_to_double(scale);
0177 
0178     // the fractional scaling protocol only speaks in unit of 120ths
0179     // using the same scale throughout makes that simpler
0180     // this also eliminates most loss from wl_fixed
0181     doubleScale = std::round(doubleScale * 120) / 120;
0182 
0183     if (doubleScale <= 0) {
0184         qCWarning(KWIN_CORE) << "Requested to scale output device to" << doubleScale << ", but I can't do that.";
0185         return;
0186     }
0187 
0188     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0189         config.changeSet(output->handle())->scale = doubleScale;
0190     }
0191 }
0192 
0193 void OutputConfigurationV2Interface::kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan)
0194 {
0195     if (invalid) {
0196         return;
0197     }
0198     if (overscan > 100) {
0199         qCWarning(KWIN_CORE) << "Invalid overscan requested:" << overscan;
0200         return;
0201     }
0202     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0203         config.changeSet(output->handle())->overscan = overscan;
0204     }
0205 }
0206 
0207 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_vrr_policy(Resource *resource, wl_resource *outputdevice, uint32_t policy)
0208 {
0209     if (invalid) {
0210         return;
0211     }
0212     if (policy > static_cast<uint32_t>(VrrPolicy::Automatic)) {
0213         qCWarning(KWIN_CORE) << "Invalid Vrr Policy requested:" << policy;
0214         return;
0215     }
0216     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0217         config.changeSet(output->handle())->vrrPolicy = static_cast<VrrPolicy>(policy);
0218     }
0219 }
0220 
0221 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange)
0222 {
0223     if (invalid) {
0224         return;
0225     }
0226     if (rgbRange > static_cast<uint32_t>(Output::RgbRange::Limited)) {
0227         qCWarning(KWIN_CORE) << "Invalid Rgb Range requested:" << rgbRange;
0228         return;
0229     }
0230     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0231         config.changeSet(output->handle())->rgbRange = static_cast<Output::RgbRange>(rgbRange);
0232     }
0233 }
0234 
0235 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output)
0236 {
0237     // intentionally ignored
0238 }
0239 
0240 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *outputResource, uint32_t priority)
0241 {
0242     if (invalid) {
0243         return;
0244     }
0245     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputResource)) {
0246         outputOrder.push_back(std::make_pair(priority, output));
0247     }
0248 }
0249 
0250 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr)
0251 {
0252     if (invalid) {
0253         return;
0254     }
0255     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0256         config.changeSet(output->handle())->highDynamicRange = enable_hdr == 1;
0257     }
0258 }
0259 
0260 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness)
0261 {
0262     if (invalid) {
0263         return;
0264     }
0265     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0266         config.changeSet(output->handle())->sdrBrightness = sdr_brightness;
0267     }
0268 }
0269 
0270 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg)
0271 {
0272     if (invalid) {
0273         return;
0274     }
0275     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0276         config.changeSet(output->handle())->wideColorGamut = enable_wcg == 1;
0277     }
0278 }
0279 
0280 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy)
0281 {
0282     if (invalid) {
0283         return;
0284     }
0285     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0286         config.changeSet(output->handle())->autoRotationPolicy = static_cast<Output::AutoRotationPolicy>(auto_rotation_policy);
0287     }
0288 }
0289 
0290 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path)
0291 {
0292     if (invalid) {
0293         return;
0294     }
0295     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0296         const auto set = config.changeSet(output->handle());
0297         set->iccProfilePath = profile_path;
0298         set->iccProfile = IccProfile::load(profile_path);
0299     }
0300 }
0301 
0302 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_brightness_overrides(Resource *resource, wl_resource *outputdevice, int32_t max_peak_brightness, int32_t max_average_brightness, int32_t min_brightness)
0303 {
0304     if (invalid) {
0305         return;
0306     }
0307     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0308         config.changeSet(output->handle())->maxPeakBrightnessOverride = max_peak_brightness == -1 ? std::nullopt : std::optional<double>(max_peak_brightness);
0309         config.changeSet(output->handle())->maxAverageBrightnessOverride = max_average_brightness == -1 ? std::nullopt : std::optional<double>(max_average_brightness);
0310         config.changeSet(output->handle())->minBrightnessOverride = min_brightness == -1 ? std::nullopt : std::optional<double>(min_brightness / 10'000.0);
0311     }
0312 }
0313 
0314 void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness)
0315 {
0316     if (invalid) {
0317         return;
0318     }
0319     if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
0320         config.changeSet(output->handle())->sdrGamutWideness = gamut_wideness / 10'000.0;
0321     }
0322 }
0323 
0324 void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy(Resource *resource)
0325 {
0326     wl_resource_destroy(resource->handle);
0327 }
0328 
0329 void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy_resource(Resource *resource)
0330 {
0331     delete this;
0332 }
0333 
0334 void OutputConfigurationV2Interface::kde_output_configuration_v2_apply(Resource *resource)
0335 {
0336     if (applied) {
0337         wl_resource_post_error(resource->handle, error_already_applied, "an output configuration can be applied only once");
0338         return;
0339     }
0340 
0341     applied = true;
0342     if (invalid) {
0343         qCWarning(KWIN_CORE) << "Rejecting configuration change because a request output is no longer available";
0344         send_failed();
0345         return;
0346     }
0347 
0348     const auto allOutputs = kwinApp()->outputBackend()->outputs();
0349     const bool allDisabled = !std::any_of(allOutputs.begin(), allOutputs.end(), [this](const auto &output) {
0350         const auto changeset = config.constChangeSet(output);
0351         if (changeset && changeset->enabled.has_value()) {
0352             return *changeset->enabled;
0353         } else {
0354             return output->isEnabled();
0355         }
0356     });
0357     if (allDisabled) {
0358         qCWarning(KWIN_CORE) << "Disabling all outputs through configuration changes is not allowed";
0359         send_failed();
0360         return;
0361     }
0362 
0363     QList<Output *> sortedOrder;
0364     if (!outputOrder.empty()) {
0365         const int desktopOutputs = std::count_if(allOutputs.begin(), allOutputs.end(), [](Output *output) {
0366             return !output->isNonDesktop();
0367         });
0368         if (outputOrder.size() != desktopOutputs) {
0369             qWarning(KWIN_CORE) << "Provided output order doesn't contain all outputs!";
0370             send_failed();
0371             return;
0372         }
0373         outputOrder.erase(std::remove_if(outputOrder.begin(), outputOrder.end(), [this](const auto &pair) {
0374                               const auto changeset = config.constChangeSet(pair.second->handle());
0375                               if (changeset && changeset->enabled.has_value()) {
0376                                   return !changeset->enabled.value();
0377                               } else {
0378                                   return !pair.second->handle()->isEnabled();
0379                               }
0380                           }),
0381                           outputOrder.end());
0382         std::sort(outputOrder.begin(), outputOrder.end(), [](const auto &pair1, const auto &pair2) {
0383             return pair1.first < pair2.first;
0384         });
0385         uint32_t i = 1;
0386         for (const auto &[index, name] : std::as_const(outputOrder)) {
0387             if (index != i) {
0388                 qCWarning(KWIN_CORE) << "Provided output order is invalid!";
0389                 send_failed();
0390                 return;
0391             }
0392             i++;
0393         }
0394         sortedOrder.reserve(outputOrder.size());
0395         std::transform(outputOrder.begin(), outputOrder.end(), std::back_inserter(sortedOrder), [](const auto &pair) {
0396             return pair.second->handle();
0397         });
0398     }
0399     if (workspace()->applyOutputConfiguration(config, sortedOrder)) {
0400         send_applied();
0401     } else {
0402         qCDebug(KWIN_CORE) << "Applying config failed";
0403         send_failed();
0404     }
0405 }
0406 
0407 }
0408 #include "outputmanagement_v2.moc"
0409 
0410 #include "moc_outputmanagement_v2.cpp"