Changeset View
Changeset View
Standalone View
Standalone View
backends/kwayland/waylandoutput.cpp
- This file was copied to backends/kwayland/plugins/kwayland/kwayland_output.cpp.
1 | /************************************************************************************* | 1 | /************************************************************************* | ||
---|---|---|---|---|---|
2 | * Copyright 2014-2015 Sebastian Kügler <sebas@kde.org> * | 2 | Copyright © 2014-2015 Sebastian Kügler <sebas@kde.org> | ||
3 | * * | 3 | Copyright © 2019-2020 Roman Gilg <subdiff@gmail.com> | ||
4 | * This library is free software; you can redistribute it and/or * | 4 | | ||
5 | * modify it under the terms of the GNU Lesser General Public * | 5 | This library is free software; you can redistribute it and/or | ||
6 | * License as published by the Free Software Foundation; either * | 6 | modify it under the terms of the GNU Lesser General Public | ||
7 | * version 2.1 of the License, or (at your option) any later version. * | 7 | License as published by the Free Software Foundation; either | ||
8 | * * | 8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | * This library is distributed in the hope that it will be useful, * | 9 | | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | 10 | This library is distributed in the hope that it will be useful, | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * Lesser General Public License for more details. * | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * * | 13 | Lesser General Public License for more details. | ||
14 | * You should have received a copy of the GNU Lesser General Public * | 14 | | ||
15 | * License along with this library; if not, write to the Free Software * | 15 | You should have received a copy of the GNU Lesser General Public | ||
16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * | 16 | License along with this library; if not, write to the Free Software | ||
17 | *************************************************************************************/ | 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | **************************************************************************/ | ||||
18 | #include "waylandoutput.h" | 19 | #include "waylandoutput.h" | ||
19 | #include "waylandbackend.h" | 20 | #include "waylandbackend.h" | ||
20 | #include "waylandconfig.h" | 21 | #include "waylandconfig.h" | ||
21 | #include "../utils.h" | 22 | #include "../utils.h" | ||
22 | 23 | | |||
23 | #include <mode.h> | 24 | #include <mode.h> | ||
24 | #include <edid.h> | 25 | #include <edid.h> | ||
25 | 26 | | |||
26 | #include <KWayland/Client/outputconfiguration.h> | | |||
27 | #include <KWayland/Client/outputdevice.h> | | |||
28 | | ||||
29 | using namespace KScreen; | 27 | using namespace KScreen; | ||
30 | namespace Wl = KWayland::Client; | | |||
31 | | ||||
32 | const QMap<Wl::OutputDevice::Transform, Output::Rotation> | | |||
33 | s_rotationMap = { | | |||
34 | {Wl::OutputDevice::Transform::Normal, Output::None}, | | |||
35 | {Wl::OutputDevice::Transform::Rotated90, Output::Right}, | | |||
36 | {Wl::OutputDevice::Transform::Rotated180, Output::Inverted}, | | |||
37 | {Wl::OutputDevice::Transform::Rotated270, Output::Left}, | | |||
38 | {Wl::OutputDevice::Transform::Flipped, Output::None}, | | |||
39 | {Wl::OutputDevice::Transform::Flipped90, Output::Right}, | | |||
40 | {Wl::OutputDevice::Transform::Flipped180, Output::Inverted}, | | |||
41 | {Wl::OutputDevice::Transform::Flipped270, Output::Left} | | |||
42 | }; | | |||
43 | | ||||
44 | Output::Rotation toKScreenRotation(const Wl::OutputDevice::Transform transform) | | |||
45 | { | | |||
46 | auto it = s_rotationMap.constFind(transform); | | |||
47 | return it.value(); | | |||
48 | } | | |||
49 | | ||||
50 | Wl::OutputDevice::Transform toKWaylandTransform(const Output::Rotation rotation) | | |||
51 | { | | |||
52 | return s_rotationMap.key(rotation); | | |||
53 | } | | |||
54 | 28 | | |||
55 | WaylandOutput::WaylandOutput(quint32 id, WaylandConfig *parent) | 29 | WaylandOutput::WaylandOutput(quint32 id, QObject *parent) | ||
56 | : QObject(parent) | 30 | : QObject(parent) | ||
57 | , m_id(id) | 31 | , m_id(id) | ||
58 | , m_device(nullptr) | | |||
59 | { | 32 | { | ||
60 | } | 33 | } | ||
61 | 34 | | |||
62 | quint32 WaylandOutput::id() const | 35 | quint32 WaylandOutput::id() const | ||
63 | { | 36 | { | ||
64 | Q_ASSERT(m_device); | | |||
65 | return m_id; | 37 | return m_id; | ||
66 | } | 38 | } | ||
67 | 39 | | |||
68 | bool WaylandOutput::enabled() const | | |||
69 | { | | |||
70 | return m_device != nullptr; | | |||
71 | } | | |||
72 | | ||||
73 | Wl::OutputDevice* WaylandOutput::outputDevice() const | | |||
74 | { | | |||
75 | return m_device; | | |||
76 | } | | |||
77 | | ||||
78 | void WaylandOutput::createOutputDevice(Wl::Registry *registry, quint32 name, quint32 version) | | |||
79 | { | | |||
80 | Q_ASSERT(!m_device); | | |||
81 | m_device = registry->createOutputDevice(name, version); | | |||
82 | | ||||
83 | connect(m_device, &Wl::OutputDevice::removed, this, &WaylandOutput::deviceRemoved); | | |||
84 | connect(m_device, &Wl::OutputDevice::done, this, [this]() { | | |||
85 | Q_EMIT complete(); | | |||
86 | connect(m_device, &Wl::OutputDevice::changed, this, &WaylandOutput::changed); | | |||
87 | }); | | |||
88 | } | | |||
89 | | ||||
90 | OutputPtr WaylandOutput::toKScreenOutput() | 40 | OutputPtr WaylandOutput::toKScreenOutput() | ||
91 | { | 41 | { | ||
92 | OutputPtr output(new Output()); | 42 | OutputPtr output(new Output()); | ||
93 | output->setId(m_id); | 43 | output->setId(m_id); | ||
94 | updateKScreenOutput(output); | 44 | updateKScreenOutput(output); | ||
95 | return output; | 45 | return output; | ||
96 | } | 46 | } | ||
97 | | ||||
98 | void WaylandOutput::updateKScreenOutput(OutputPtr &output) | | |||
99 | { | | |||
100 | // Initialize primary output | | |||
101 | output->setId(m_id); | | |||
102 | output->setEnabled(m_device->enabled() == Wl::OutputDevice::Enablement::Enabled); | | |||
103 | output->setConnected(true); | | |||
104 | output->setPrimary(true); // FIXME: wayland doesn't have the concept of a primary display | | |||
105 | output->setName(name()); | | |||
106 | output->setSizeMm(m_device->physicalSize()); | | |||
107 | output->setPos(m_device->globalPosition()); | | |||
108 | output->setRotation(s_rotationMap[m_device->transform()]); | | |||
109 | | ||||
110 | ModeList modeList; | | |||
111 | QStringList preferredModeIds; | | |||
112 | m_modeIdMap.clear(); | | |||
113 | QString currentModeId = QStringLiteral("-1"); | | |||
114 | | ||||
115 | for (const Wl::OutputDevice::Mode &wlMode : m_device->modes()) { | | |||
116 | ModePtr mode(new Mode()); | | |||
117 | const QString name = modeName(wlMode); | | |||
118 | | ||||
119 | QString modeId = QString::number(wlMode.id); | | |||
120 | if (modeId.isEmpty()) { | | |||
121 | qCDebug(KSCREEN_WAYLAND) << "Could not create mode id from" | | |||
122 | << wlMode.id << ", using" << name << "instead."; | | |||
123 | modeId = name; | | |||
124 | } | | |||
125 | | ||||
126 | if (m_modeIdMap.contains(modeId)) { | | |||
127 | qCWarning(KSCREEN_WAYLAND) << "Mode id already in use:" << modeId; | | |||
128 | } | | |||
129 | mode->setId(modeId); | | |||
130 | | ||||
131 | // KWayland gives the refresh rate as int in mHz | | |||
132 | mode->setRefreshRate(wlMode.refreshRate / 1000.0); | | |||
133 | mode->setSize(wlMode.size); | | |||
134 | mode->setName(name); | | |||
135 | | ||||
136 | if (wlMode.flags.testFlag(Wl::OutputDevice::Mode::Flag::Current)) { | | |||
137 | currentModeId = modeId; | | |||
138 | } | | |||
139 | if (wlMode.flags.testFlag(Wl::OutputDevice::Mode::Flag::Preferred)) { | | |||
140 | preferredModeIds << modeId; | | |||
141 | } | | |||
142 | | ||||
143 | // Update the kscreen => kwayland mode id translation map | | |||
144 | m_modeIdMap.insert(modeId, wlMode.id); | | |||
145 | // Add to the modelist which gets set on the output | | |||
146 | modeList[modeId] = mode; | | |||
147 | } | | |||
148 | | ||||
149 | if (currentModeId == QLatin1String("-1")) { | | |||
150 | qCWarning(KSCREEN_WAYLAND) << "Could not find the current mode id" << modeList; | | |||
151 | } | | |||
152 | | ||||
153 | output->setCurrentModeId(currentModeId); | | |||
154 | output->setPreferredModes(preferredModeIds); | | |||
155 | output->setModes(modeList); | | |||
156 | output->setScale(m_device->scaleF()); | | |||
157 | output->setType(Utils::guessOutputType(m_device->model(), m_device->model())); | | |||
158 | } | | |||
159 | | ||||
160 | bool WaylandOutput::setWlConfig(Wl::OutputConfiguration *wlConfig, | | |||
161 | const KScreen::OutputPtr &output) | | |||
162 | { | | |||
163 | bool changed = false; | | |||
164 | | ||||
165 | // enabled? | | |||
166 | if ((m_device->enabled() == Wl::OutputDevice::Enablement::Enabled) | | |||
167 | != output->isEnabled()) { | | |||
168 | changed = true; | | |||
169 | const auto enablement = output->isEnabled() ? Wl::OutputDevice::Enablement::Enabled : | | |||
170 | Wl::OutputDevice::Enablement::Disabled; | | |||
171 | wlConfig->setEnabled(m_device, enablement); | | |||
172 | } | | |||
173 | | ||||
174 | // position | | |||
175 | if (m_device->globalPosition() != output->pos()) { | | |||
176 | changed = true; | | |||
177 | wlConfig->setPosition(m_device, output->pos()); | | |||
178 | } | | |||
179 | | ||||
180 | // scale | | |||
181 | if (!qFuzzyCompare(m_device->scaleF(), output->scale())) { | | |||
182 | changed = true; | | |||
183 | wlConfig->setScaleF(m_device, output->scale()); | | |||
184 | } | | |||
185 | | ||||
186 | // rotation | | |||
187 | if (toKScreenRotation(m_device->transform()) != output->rotation()) { | | |||
188 | changed = true; | | |||
189 | wlConfig->setTransform(m_device, toKWaylandTransform(output->rotation())); | | |||
190 | } | | |||
191 | | ||||
192 | // mode | | |||
193 | if (m_modeIdMap.contains(output->currentModeId())) { | | |||
194 | const int newModeId = m_modeIdMap.value(output->currentModeId(), -1); | | |||
195 | if (newModeId != m_device->currentMode().id) { | | |||
196 | changed = true; | | |||
197 | wlConfig->setMode(m_device, newModeId); | | |||
198 | } | | |||
199 | } else { | | |||
200 | qCWarning(KSCREEN_WAYLAND) << "Invalid kscreen mode id:" << output->currentModeId() | | |||
201 | << "\n\n" << m_modeIdMap; | | |||
202 | } | | |||
203 | return changed; | | |||
204 | } | | |||
205 | | ||||
206 | QString WaylandOutput::modeName(const Wl::OutputDevice::Mode &m) const | | |||
207 | { | | |||
208 | return QString::number(m.size.width()) + QLatin1Char('x') + | | |||
209 | QString::number(m.size.height()) + QLatin1Char('@') + | | |||
210 | QString::number(qRound(m.refreshRate/1000.0)); | | |||
211 | } | | |||
212 | | ||||
213 | QString WaylandOutput::name() const | | |||
214 | { | | |||
215 | Q_ASSERT(m_device); | | |||
216 | return QStringLiteral("%1 %2").arg(m_device->manufacturer(), m_device->model()); | | |||
217 | } | | |||
218 | | ||||
219 | QDebug operator<<(QDebug dbg, const WaylandOutput *output) | | |||
220 | { | | |||
221 | dbg << "WaylandOutput(Id:" << output->id() <<", Name:" << \ | | |||
222 | QString(output->outputDevice()->manufacturer() + QLatin1Char(' ') + \ | | |||
223 | output->outputDevice()->model()) << ")"; | | |||
224 | return dbg; | | |||
225 | } | |