1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
6     SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
7 
8     SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 #include "abstract_wayland_output.h"
11 #include "screens.h"
12 
13 // KWayland
14 #include <KWaylandServer/outputchangeset_v2.h>
15 // KF5
16 #include <KLocalizedString>
17 
18 #include <QMatrix4x4>
19 
20 namespace KWin
21 {
22 
outputDeviceTransformToKWinTransform(KWaylandServer::OutputDeviceV2Interface::Transform transform)23 static AbstractWaylandOutput::Transform outputDeviceTransformToKWinTransform(KWaylandServer::OutputDeviceV2Interface::Transform transform)
24 {
25     return static_cast<AbstractWaylandOutput::Transform>(transform);
26 }
27 
AbstractWaylandOutput(QObject * parent)28 AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent)
29     : AbstractOutput(parent)
30 {
31 }
32 
capabilities() const33 AbstractWaylandOutput::Capabilities AbstractWaylandOutput::capabilities() const
34 {
35     return m_capabilities;
36 }
37 
setCapabilityInternal(Capability capability,bool on)38 void AbstractWaylandOutput::setCapabilityInternal(Capability capability, bool on)
39 {
40     if (static_cast<bool>(m_capabilities & capability) != on) {
41         m_capabilities.setFlag(capability, on);
42         Q_EMIT capabilitiesChanged();
43     }
44 }
45 
name() const46 QString AbstractWaylandOutput::name() const
47 {
48     return m_name;
49 }
50 
uuid() const51 QUuid AbstractWaylandOutput::uuid() const
52 {
53     return m_uuid;
54 }
55 
geometry() const56 QRect AbstractWaylandOutput::geometry() const
57 {
58     return QRect(m_position, pixelSize() / scale());
59 }
60 
physicalSize() const61 QSize AbstractWaylandOutput::physicalSize() const
62 {
63     return orientateSize(m_physicalSize);
64 }
65 
refreshRate() const66 int AbstractWaylandOutput::refreshRate() const
67 {
68     return m_refreshRate;
69 }
70 
moveTo(const QPoint & pos)71 void AbstractWaylandOutput::moveTo(const QPoint &pos)
72 {
73     if (m_position != pos) {
74         m_position = pos;
75         Q_EMIT geometryChanged();
76     }
77 }
78 
eisaId() const79 QString AbstractWaylandOutput::eisaId() const
80 {
81     return m_eisaId;
82 }
83 
manufacturer() const84 QString AbstractWaylandOutput::manufacturer() const
85 {
86     return m_manufacturer;
87 }
88 
model() const89 QString AbstractWaylandOutput::model() const
90 {
91     return m_model;
92 }
93 
serialNumber() const94 QString AbstractWaylandOutput::serialNumber() const
95 {
96     return m_serialNumber;
97 }
98 
modeSize() const99 QSize AbstractWaylandOutput::modeSize() const
100 {
101     return m_modeSize;
102 }
103 
pixelSize() const104 QSize AbstractWaylandOutput::pixelSize() const
105 {
106     return orientateSize(m_modeSize);
107 }
108 
edid() const109 QByteArray AbstractWaylandOutput::edid() const
110 {
111     return m_edid;
112 }
113 
operator ==(const Mode & other) const114 bool AbstractWaylandOutput::Mode::operator==(const Mode &other) const {
115     return id == other.id && other.flags == flags && size == other.size && refreshRate == other.refreshRate;
116 }
117 
modes() const118 QVector<AbstractWaylandOutput::Mode> AbstractWaylandOutput::modes() const
119 {
120     return m_modes;
121 }
122 
setModes(const QVector<Mode> & modes)123 void AbstractWaylandOutput::setModes(const QVector<Mode> &modes)
124 {
125     if (m_modes != modes) {
126         m_modes = modes;
127         Q_EMIT modesChanged();
128     }
129 }
130 
scale() const131 qreal AbstractWaylandOutput::scale() const
132 {
133     return m_scale;
134 }
135 
setScale(qreal scale)136 void AbstractWaylandOutput::setScale(qreal scale)
137 {
138     if (m_scale != scale) {
139         m_scale = scale;
140         Q_EMIT scaleChanged();
141         Q_EMIT geometryChanged();
142     }
143 }
144 
subPixel() const145 AbstractWaylandOutput::SubPixel AbstractWaylandOutput::subPixel() const
146 {
147     return m_subPixel;
148 }
149 
setSubPixelInternal(SubPixel subPixel)150 void AbstractWaylandOutput::setSubPixelInternal(SubPixel subPixel)
151 {
152     m_subPixel = subPixel;
153 }
154 
applyChanges(const KWaylandServer::OutputChangeSetV2 * changeSet)155 void AbstractWaylandOutput::applyChanges(const KWaylandServer::OutputChangeSetV2 *changeSet)
156 {
157     Q_EMIT aboutToChange();
158 
159     qCDebug(KWIN_CORE) << "Apply changes to the Wayland output.";
160     bool emitModeChanged = false;
161     bool overallSizeCheckNeeded = false;
162 
163     // Enablement changes are handled by platform.
164     if (changeSet->sizeChanged() || changeSet->refreshRateChanged()) {
165         qCDebug(KWIN_CORE) << "Setting new mode:" << changeSet->size() << changeSet->refreshRate();
166         updateMode(changeSet->size(), changeSet->refreshRate());
167         emitModeChanged = true;
168     }
169     if (changeSet->transformChanged()) {
170         qCDebug(KWIN_CORE) << "Server setting transform: " << changeSet->transform();
171         auto transform = outputDeviceTransformToKWinTransform(changeSet->transform());
172         updateTransform(transform);
173         emitModeChanged = true;
174     }
175     if (changeSet->positionChanged()) {
176         qCDebug(KWIN_CORE) << "Server setting position: " << changeSet->position();
177         moveTo(changeSet->position());
178         // may just work already!
179         overallSizeCheckNeeded = true;
180     }
181     if (changeSet->scaleChanged()) {
182         qCDebug(KWIN_CORE) << "Setting scale:" << changeSet->scale();
183         setScale(changeSet->scale());
184         emitModeChanged = true;
185     }
186     if (changeSet->overscanChanged()) {
187         qCDebug(KWIN_CORE) << "Setting overscan:" << changeSet->overscan();
188         setOverscan(changeSet->overscan());
189     }
190     if (changeSet->vrrPolicyChanged()) {
191         qCDebug(KWIN_CORE) << "Setting VRR Policy:" << changeSet->vrrPolicy();
192         setVrrPolicy(static_cast<RenderLoop::VrrPolicy>(changeSet->vrrPolicy()));
193     }
194     if (changeSet->rgbRangeChanged()) {
195         qDebug(KWIN_CORE) << "Setting rgb range:" << changeSet->rgbRange();
196         setRgbRange(static_cast<AbstractWaylandOutput::RgbRange>(changeSet->rgbRange()));
197     }
198     Q_EMIT changed();
199 
200     overallSizeCheckNeeded |= emitModeChanged;
201     if (overallSizeCheckNeeded) {
202         Q_EMIT screens()->changed();
203     }
204 
205     if (emitModeChanged) {
206         Q_EMIT currentModeChanged();
207     }
208 }
209 
isEnabled() const210 bool AbstractWaylandOutput::isEnabled() const
211 {
212     return m_isEnabled;
213 }
214 
setEnabled(bool enable)215 void AbstractWaylandOutput::setEnabled(bool enable)
216 {
217     if (m_isEnabled != enable) {
218         m_isEnabled = enable;
219         updateEnablement(enable);
220         Q_EMIT enabledChanged();
221     }
222 }
223 
description() const224 QString AbstractWaylandOutput::description() const
225 {
226     return m_manufacturer + ' ' + m_model;
227 }
228 
setCurrentModeInternal(const QSize & size,int refreshRate)229 void AbstractWaylandOutput::setCurrentModeInternal(const QSize &size, int refreshRate)
230 {
231     const bool sizeChanged = m_modeSize != size;
232     if (sizeChanged || m_refreshRate != refreshRate) {
233         m_modeSize = size;
234         m_refreshRate = refreshRate;
235 
236         Q_EMIT currentModeChanged();
237         if (sizeChanged) {
238             Q_EMIT geometryChanged();
239         }
240     }
241 }
242 
generateOutputId(const QString & eisaId,const QString & model,const QString & serialNumber,const QString & name)243 static QUuid generateOutputId(const QString &eisaId, const QString &model,
244                               const QString &serialNumber, const QString &name)
245 {
246     static const QUuid urlNs = QUuid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); // NameSpace_URL
247     static const QUuid kwinNs = QUuid::createUuidV5(urlNs, QStringLiteral("https://kwin.kde.org/o/"));
248 
249     const QString payload = QStringList{name, eisaId, model, serialNumber}.join(':');
250     return QUuid::createUuidV5(kwinNs, payload);
251 }
252 
initialize(const QString & model,const QString & manufacturer,const QString & eisaId,const QString & serialNumber,const QSize & physicalSize,const QVector<Mode> & modes,const QByteArray & edid)253 void AbstractWaylandOutput::initialize(const QString &model, const QString &manufacturer,
254                                        const QString &eisaId, const QString &serialNumber,
255                                        const QSize &physicalSize,
256                                        const QVector<Mode> &modes, const QByteArray &edid)
257 {
258     m_serialNumber = serialNumber;
259     m_eisaId = eisaId;
260     m_manufacturer = manufacturer.isEmpty() ? i18n("unknown") : manufacturer;
261     m_model = model;
262     m_physicalSize = physicalSize;
263     m_edid = edid;
264     m_modes = modes;
265     m_uuid = generateOutputId(m_eisaId, m_model, m_serialNumber, m_name);
266 
267     for (const Mode &mode : modes) {
268         if (mode.flags & ModeFlag::Current) {
269             m_modeSize = mode.size;
270             m_refreshRate = mode.refreshRate;
271             break;
272         }
273     }
274 }
275 
orientateSize(const QSize & size) const276 QSize AbstractWaylandOutput::orientateSize(const QSize &size) const
277 {
278     if (m_transform == Transform::Rotated90 || m_transform == Transform::Rotated270 ||
279             m_transform == Transform::Flipped90 || m_transform == Transform::Flipped270) {
280         return size.transposed();
281     }
282     return size;
283 }
284 
setTransformInternal(Transform transform)285 void AbstractWaylandOutput::setTransformInternal(Transform transform)
286 {
287     if (m_transform != transform) {
288         m_transform = transform;
289         Q_EMIT transformChanged();
290         Q_EMIT currentModeChanged();
291         Q_EMIT geometryChanged();
292     }
293 }
294 
transform() const295 AbstractWaylandOutput::Transform AbstractWaylandOutput::transform() const
296 {
297     return m_transform;
298 }
299 
setDpmsModeInternal(DpmsMode dpmsMode)300 void AbstractWaylandOutput::setDpmsModeInternal(DpmsMode dpmsMode)
301 {
302     if (m_dpmsMode != dpmsMode) {
303         m_dpmsMode = dpmsMode;
304         Q_EMIT dpmsModeChanged();
305     }
306 }
307 
setDpmsMode(DpmsMode mode)308 void AbstractWaylandOutput::setDpmsMode(DpmsMode mode)
309 {
310     Q_UNUSED(mode)
311 }
312 
dpmsMode() const313 AbstractWaylandOutput::DpmsMode AbstractWaylandOutput::dpmsMode() const
314 {
315     return m_dpmsMode;
316 }
317 
logicalToNativeMatrix(const QRect & rect,qreal scale,Transform transform)318 QMatrix4x4 AbstractWaylandOutput::logicalToNativeMatrix(const QRect &rect, qreal scale, Transform transform)
319 {
320     QMatrix4x4 matrix;
321     matrix.scale(scale);
322 
323     switch (transform) {
324     case Transform::Normal:
325     case Transform::Flipped:
326         break;
327     case Transform::Rotated90:
328     case Transform::Flipped90:
329         matrix.translate(0, rect.width());
330         matrix.rotate(-90, 0, 0, 1);
331         break;
332     case Transform::Rotated180:
333     case Transform::Flipped180:
334         matrix.translate(rect.width(), rect.height());
335         matrix.rotate(-180, 0, 0, 1);
336         break;
337     case Transform::Rotated270:
338     case Transform::Flipped270:
339         matrix.translate(rect.height(), 0);
340         matrix.rotate(-270, 0, 0, 1);
341         break;
342     }
343 
344     switch (transform) {
345     case Transform::Flipped:
346     case Transform::Flipped90:
347     case Transform::Flipped180:
348     case Transform::Flipped270:
349         matrix.translate(rect.width(), 0);
350         matrix.scale(-1, 1);
351         break;
352     default:
353         break;
354     }
355 
356     matrix.translate(-rect.x(), -rect.y());
357 
358     return matrix;
359 }
360 
recordingStarted()361 void AbstractWaylandOutput::recordingStarted()
362 {
363     m_recorders++;
364 }
365 
recordingStopped()366 void AbstractWaylandOutput::recordingStopped()
367 {
368     m_recorders--;
369 }
370 
isBeingRecorded()371 bool AbstractWaylandOutput::isBeingRecorded()
372 {
373     return m_recorders;
374 }
375 
setOverscanInternal(uint32_t overscan)376 void AbstractWaylandOutput::setOverscanInternal(uint32_t overscan)
377 {
378     if (m_overscan != overscan) {
379         m_overscan = overscan;
380         Q_EMIT overscanChanged();
381     }
382 }
383 
overscan() const384 uint32_t AbstractWaylandOutput::overscan() const
385 {
386     return m_overscan;
387 }
388 
setOverscan(uint32_t overscan)389 void AbstractWaylandOutput::setOverscan(uint32_t overscan)
390 {
391     Q_UNUSED(overscan);
392 }
393 
setVrrPolicy(RenderLoop::VrrPolicy policy)394 void AbstractWaylandOutput::setVrrPolicy(RenderLoop::VrrPolicy policy)
395 {
396     if (renderLoop()->vrrPolicy() != policy && (m_capabilities & Capability::Vrr)) {
397         renderLoop()->setVrrPolicy(policy);
398         Q_EMIT vrrPolicyChanged();
399     }
400 }
401 
vrrPolicy() const402 RenderLoop::VrrPolicy AbstractWaylandOutput::vrrPolicy() const
403 {
404     return renderLoop()->vrrPolicy();
405 }
406 
isPlaceholder() const407 bool AbstractWaylandOutput::isPlaceholder() const
408 {
409     return m_isPlaceholder;
410 }
411 
setPlaceholder(bool isPlaceholder)412 void AbstractWaylandOutput::setPlaceholder(bool isPlaceholder)
413 {
414     m_isPlaceholder = isPlaceholder;
415 }
416 
rgbRange() const417 AbstractWaylandOutput::RgbRange AbstractWaylandOutput::rgbRange() const
418 {
419     return m_rgbRange;
420 }
421 
setRgbRange(RgbRange range)422 void AbstractWaylandOutput::setRgbRange(RgbRange range)
423 {
424     Q_UNUSED(range)
425 }
426 
setRgbRangeInternal(RgbRange range)427 void AbstractWaylandOutput::setRgbRangeInternal(RgbRange range)
428 {
429     if (m_rgbRange != range) {
430         m_rgbRange = range;
431         Q_EMIT rgbRangeChanged();
432     }
433 }
434 
435 }
436