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