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"