File indexing completed on 2024-11-10 04:57:29
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"