KWin
Loading...
Searching...
No Matches
outputmanagement_v2.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
8#include "core/iccprofile.h"
11#include "display.h"
12#include "main.h"
13#include "outputdevice_v2.h"
14#include "outputmanagement_v2.h"
15#include "utils/common.h"
16#include "workspace.h"
17
18#include "qwayland-server-kde-output-management-v2.h"
19
20#include <cmath>
21#include <optional>
22
23namespace KWin
24{
25
26static const quint32 s_version = 7;
27
28class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2
29{
30public:
32
33protected:
34 void kde_output_management_v2_create_configuration(Resource *resource, uint32_t id) override;
35};
36
37class OutputConfigurationV2Interface : public QObject, QtWaylandServer::kde_output_configuration_v2
38{
39 Q_OBJECT
40public:
41 explicit OutputConfigurationV2Interface(wl_resource *resource);
42
43 bool applied = false;
44 bool invalid = false;
46 QList<std::pair<uint32_t, OutputDeviceV2Interface *>> outputOrder;
47
48protected:
49 void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override;
50 void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override;
51 void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override;
52 void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override;
53 void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override;
54 void kde_output_configuration_v2_apply(Resource *resource) override;
55 void kde_output_configuration_v2_destroy(Resource *resource) override;
56 void kde_output_configuration_v2_destroy_resource(Resource *resource) override;
57 void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override;
58 void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override;
59 void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override;
60 void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override;
61 void kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *output, uint32_t priority) override;
62 void kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr) override;
63 void kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness) override;
64 void kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg) override;
65 void kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy) override;
66 void kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path) override;
67 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;
68 void kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness) override;
69};
70
72 : QtWaylandServer::kde_output_management_v2(*display, s_version)
73{
74}
75
77{
78 wl_resource *config_resource = wl_resource_create(resource->client(), &kde_output_configuration_v2_interface, resource->version(), id);
79 if (!config_resource) {
80 wl_client_post_no_memory(resource->client());
81 return;
82 }
83 new OutputConfigurationV2Interface(config_resource);
84}
85
87 : QObject(parent)
89{
90}
91
93
95 : QtWaylandServer::kde_output_configuration_v2(resource)
96{
97 const auto reject = [this](Output *output) {
98 invalid = true;
99 };
100 connect(workspace(), &Workspace::outputAdded, this, reject);
101 connect(workspace(), &Workspace::outputRemoved, this, reject);
102}
103
104void OutputConfigurationV2Interface::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable)
105{
106 if (invalid) {
107 return;
108 }
109 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
110 config.changeSet(output->handle())->enabled = enable;
111 }
112}
113
114void OutputConfigurationV2Interface::kde_output_configuration_v2_mode(Resource *resource, wl_resource *outputdevice, wl_resource *modeResource)
115{
116 if (invalid) {
117 return;
118 }
121 if (output && mode) {
122 config.changeSet(output->handle())->mode = mode->handle().lock();
123 } else {
124 invalid = true;
125 }
126}
127
128void OutputConfigurationV2Interface::kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform)
129{
130 if (invalid) {
131 return;
132 }
133 auto toTransform = [transform]() {
134 switch (transform) {
135 case WL_OUTPUT_TRANSFORM_90:
137 case WL_OUTPUT_TRANSFORM_180:
139 case WL_OUTPUT_TRANSFORM_270:
141 case WL_OUTPUT_TRANSFORM_FLIPPED:
143 case WL_OUTPUT_TRANSFORM_FLIPPED_90:
145 case WL_OUTPUT_TRANSFORM_FLIPPED_180:
147 case WL_OUTPUT_TRANSFORM_FLIPPED_270:
149 case WL_OUTPUT_TRANSFORM_NORMAL:
150 default:
152 }
153 };
154 auto _transform = toTransform();
155 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
156 const auto changeset = config.changeSet(output->handle());
157 changeset->transform = changeset->manualTransform = _transform;
158 }
159}
160
161void OutputConfigurationV2Interface::kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y)
162{
163 if (invalid) {
164 return;
165 }
166 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
167 config.changeSet(output->handle())->pos = QPoint(x, y);
168 }
169}
170
171void OutputConfigurationV2Interface::kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale)
172{
173 if (invalid) {
174 return;
175 }
176 qreal doubleScale = wl_fixed_to_double(scale);
177
178 // the fractional scaling protocol only speaks in unit of 120ths
179 // using the same scale throughout makes that simpler
180 // this also eliminates most loss from wl_fixed
181 doubleScale = std::round(doubleScale * 120) / 120;
182
183 if (doubleScale <= 0) {
184 qCWarning(KWIN_CORE) << "Requested to scale output device to" << doubleScale << ", but I can't do that.";
185 return;
186 }
187
188 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
189 config.changeSet(output->handle())->scale = doubleScale;
190 }
191}
192
193void OutputConfigurationV2Interface::kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan)
194{
195 if (invalid) {
196 return;
197 }
198 if (overscan > 100) {
199 qCWarning(KWIN_CORE) << "Invalid overscan requested:" << overscan;
200 return;
201 }
202 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
203 config.changeSet(output->handle())->overscan = overscan;
204 }
205}
206
207void OutputConfigurationV2Interface::kde_output_configuration_v2_set_vrr_policy(Resource *resource, wl_resource *outputdevice, uint32_t policy)
208{
209 if (invalid) {
210 return;
211 }
212 if (policy > static_cast<uint32_t>(VrrPolicy::Automatic)) {
213 qCWarning(KWIN_CORE) << "Invalid Vrr Policy requested:" << policy;
214 return;
215 }
216 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
217 config.changeSet(output->handle())->vrrPolicy = static_cast<VrrPolicy>(policy);
218 }
219}
220
221void OutputConfigurationV2Interface::kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange)
222{
223 if (invalid) {
224 return;
225 }
226 if (rgbRange > static_cast<uint32_t>(Output::RgbRange::Limited)) {
227 qCWarning(KWIN_CORE) << "Invalid Rgb Range requested:" << rgbRange;
228 return;
229 }
230 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
231 config.changeSet(output->handle())->rgbRange = static_cast<Output::RgbRange>(rgbRange);
232 }
233}
234
235void OutputConfigurationV2Interface::kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output)
236{
237 // intentionally ignored
238}
239
240void OutputConfigurationV2Interface::kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *outputResource, uint32_t priority)
241{
242 if (invalid) {
243 return;
244 }
245 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputResource)) {
246 outputOrder.push_back(std::make_pair(priority, output));
247 }
248}
249
250void OutputConfigurationV2Interface::kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr)
251{
252 if (invalid) {
253 return;
254 }
255 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
256 config.changeSet(output->handle())->highDynamicRange = enable_hdr == 1;
257 }
258}
259
260void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness)
261{
262 if (invalid) {
263 return;
264 }
265 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
266 config.changeSet(output->handle())->sdrBrightness = sdr_brightness;
267 }
268}
269
270void OutputConfigurationV2Interface::kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg)
271{
272 if (invalid) {
273 return;
274 }
275 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
276 config.changeSet(output->handle())->wideColorGamut = enable_wcg == 1;
277 }
278}
279
280void OutputConfigurationV2Interface::kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy)
281{
282 if (invalid) {
283 return;
284 }
285 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
286 config.changeSet(output->handle())->autoRotationPolicy = static_cast<Output::AutoRotationPolicy>(auto_rotation_policy);
287 }
288}
289
290void OutputConfigurationV2Interface::kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path)
291{
292 if (invalid) {
293 return;
294 }
295 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
296 const auto set = config.changeSet(output->handle());
297 set->iccProfilePath = profile_path;
298 set->iccProfile = IccProfile::load(profile_path);
299 }
300}
301
302void 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)
303{
304 if (invalid) {
305 return;
306 }
307 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
308 config.changeSet(output->handle())->maxPeakBrightnessOverride = max_peak_brightness == -1 ? std::nullopt : std::optional<double>(max_peak_brightness);
309 config.changeSet(output->handle())->maxAverageBrightnessOverride = max_average_brightness == -1 ? std::nullopt : std::optional<double>(max_average_brightness);
310 config.changeSet(output->handle())->minBrightnessOverride = min_brightness == -1 ? std::nullopt : std::optional<double>(min_brightness / 10'000.0);
311 }
312}
313
314void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness)
315{
316 if (invalid) {
317 return;
318 }
319 if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
320 config.changeSet(output->handle())->sdrGamutWideness = gamut_wideness / 10'000.0;
321 }
322}
323
325{
326 wl_resource_destroy(resource->handle);
327}
328
333
335{
336 if (applied) {
337 wl_resource_post_error(resource->handle, error_already_applied, "an output configuration can be applied only once");
338 return;
339 }
340
341 applied = true;
342 if (invalid) {
343 qCWarning(KWIN_CORE) << "Rejecting configuration change because a request output is no longer available";
344 send_failed();
345 return;
346 }
347
348 const auto allOutputs = kwinApp()->outputBackend()->outputs();
349 const bool allDisabled = !std::any_of(allOutputs.begin(), allOutputs.end(), [this](const auto &output) {
350 const auto changeset = config.constChangeSet(output);
351 if (changeset && changeset->enabled.has_value()) {
352 return *changeset->enabled;
353 } else {
354 return output->isEnabled();
355 }
356 });
357 if (allDisabled) {
358 qCWarning(KWIN_CORE) << "Disabling all outputs through configuration changes is not allowed";
359 send_failed();
360 return;
361 }
362
363 QList<Output *> sortedOrder;
364 if (!outputOrder.empty()) {
365 const int desktopOutputs = std::count_if(allOutputs.begin(), allOutputs.end(), [](Output *output) {
366 return !output->isNonDesktop();
367 });
368 if (outputOrder.size() != desktopOutputs) {
369 qWarning(KWIN_CORE) << "Provided output order doesn't contain all outputs!";
370 send_failed();
371 return;
372 }
373 outputOrder.erase(std::remove_if(outputOrder.begin(), outputOrder.end(), [this](const auto &pair) {
374 const auto changeset = config.constChangeSet(pair.second->handle());
375 if (changeset && changeset->enabled.has_value()) {
376 return !changeset->enabled.value();
377 } else {
378 return !pair.second->handle()->isEnabled();
379 }
380 }),
381 outputOrder.end());
382 std::sort(outputOrder.begin(), outputOrder.end(), [](const auto &pair1, const auto &pair2) {
383 return pair1.first < pair2.first;
384 });
385 uint32_t i = 1;
386 for (const auto &[index, name] : std::as_const(outputOrder)) {
387 if (index != i) {
388 qCWarning(KWIN_CORE) << "Provided output order is invalid!";
389 send_failed();
390 return;
391 }
392 i++;
393 }
394 sortedOrder.reserve(outputOrder.size());
395 std::transform(outputOrder.begin(), outputOrder.end(), std::back_inserter(sortedOrder), [](const auto &pair) {
396 return pair.second->handle();
397 });
398 }
399 if (workspace()->applyOutputConfiguration(config, sortedOrder)) {
400 send_applied();
401 } else {
402 qCDebug(KWIN_CORE) << "Applying config failed";
403 send_failed();
404 }
405}
406
407}
408#include "outputmanagement_v2.moc"
409
410#include "moc_outputmanagement_v2.cpp"
Class holding the Wayland server display loop.
Definition display.h:34
static std::unique_ptr< IccProfile > load(const QString &path)
std::shared_ptr< OutputChangeSet > changeSet(Output *output)
void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override
void kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg) override
void kde_output_configuration_v2_destroy_resource(Resource *resource) override
void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override
void kde_output_configuration_v2_destroy(Resource *resource) override
void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override
void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override
void kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness) override
void kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness) override
void kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy) override
void kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *output, uint32_t priority) override
void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override
void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override
OutputConfigurationV2Interface(wl_resource *resource)
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
void kde_output_configuration_v2_apply(Resource *resource) override
void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override
void kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path) override
void kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr) override
void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override
void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override
QList< std::pair< uint32_t, OutputDeviceV2Interface * > > outputOrder
std::weak_ptr< OutputMode > handle() const
static OutputDeviceModeV2Interface * get(wl_resource *native)
static OutputDeviceV2Interface * get(wl_resource *native)
OutputManagementV2Interface(Display *display, QObject *parent=nullptr)
void kde_output_management_v2_create_configuration(Resource *resource, uint32_t id) override
void outputAdded(KWin::Output *)
void outputRemoved(KWin::Output *)
VrrPolicy
Definition globals.h:292
Workspace * workspace()
Definition workspace.h:830