Changeset View
Changeset View
Standalone View
Standalone View
backends/kwayland/plugins/kwayland/kwayland_interface.cpp
- This file was added.
1 | /************************************************************************* | ||||
---|---|---|---|---|---|
2 | Copyright © 2013 Martin Gräßlin <mgraesslin@kde.org> | ||||
3 | Copyright © 2014-2015 Sebastian Kügler <sebas@kde.org> | ||||
4 | Copyright © 2019-2020 Roman Gilg <subdiff@gmail.com> | ||||
5 | | ||||
6 | This library is free software; you can redistribute it and/or | ||||
7 | modify it under the terms of the GNU Lesser General Public | ||||
8 | License as published by the Free Software Foundation; either | ||||
9 | version 2.1 of the License, or (at your option) any later version. | ||||
10 | | ||||
11 | This library is distributed in the hope that it will be useful, | ||||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||||
14 | Lesser General Public License for more details. | ||||
15 | | ||||
16 | You should have received a copy of the GNU Lesser General Public | ||||
17 | License along with this library; if not, write to the Free Software | ||||
18 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||||
19 | **************************************************************************/ | ||||
20 | #include "kwayland_interface.h" | ||||
21 | | ||||
22 | #include "waylandbackend.h" | ||||
23 | #include "kwayland_output.h" | ||||
24 | #include "waylandscreen.h" | ||||
25 | | ||||
26 | #include "../../wayland_logging.h" | ||||
27 | | ||||
28 | #include <KWayland/Client/connection_thread.h> | ||||
29 | #include <KWayland/Client/event_queue.h> | ||||
30 | #include <KWayland/Client/registry.h> | ||||
31 | #include <KWayland/Client/outputconfiguration.h> | ||||
32 | #include <KWayland/Client/outputmanagement.h> | ||||
33 | | ||||
34 | #include <QThread> | ||||
35 | | ||||
36 | using namespace KScreen; | ||||
37 | | ||||
38 | | ||||
39 | WaylandInterface* KWaylandFactory::createInterface(QObject *parent) | ||||
40 | { | ||||
41 | return new KWaylandInterface(parent); | ||||
42 | } | ||||
43 | | ||||
44 | KWaylandInterface::KWaylandInterface(QObject *parent) | ||||
45 | : WaylandInterface(parent) | ||||
46 | , m_outputManagement(nullptr) | ||||
47 | , m_registryInitialized(false) | ||||
48 | , m_kscreenPendingConfig(nullptr) | ||||
49 | { | ||||
50 | } | ||||
51 | | ||||
52 | void KWaylandInterface::initConnection(QThread *thread) | ||||
53 | { | ||||
54 | m_connection = new KWayland::Client::ConnectionThread; | ||||
55 | | ||||
56 | connect(m_connection, &KWayland::Client::ConnectionThread::connected, | ||||
57 | this, &KWaylandInterface::setupRegistry, Qt::QueuedConnection); | ||||
58 | | ||||
59 | connect(m_connection, &KWayland::Client::ConnectionThread::connectionDied, | ||||
60 | this, &KWaylandInterface::handleDisconnect, Qt::QueuedConnection); | ||||
61 | | ||||
62 | connect(m_connection, &KWayland::Client::ConnectionThread::failed, this, [this] { | ||||
63 | qCWarning(KSCREEN_WAYLAND) << "Failed to connect to Wayland server at socket:" | ||||
64 | << m_connection->socketName(); | ||||
65 | Q_EMIT connectionFailed(m_connection->socketName()); | ||||
66 | }); | ||||
67 | | ||||
68 | m_connection->moveToThread(thread); | ||||
69 | thread->start(); | ||||
70 | m_connection->initConnection(); | ||||
71 | } | ||||
72 | | ||||
73 | bool KWaylandInterface::isInitialized() const | ||||
74 | { | ||||
75 | return m_registryInitialized && m_outputManagement != nullptr | ||||
76 | && WaylandInterface::isInitialized(); | ||||
77 | } | ||||
78 | | ||||
79 | void KWaylandInterface::handleDisconnect() | ||||
80 | { | ||||
81 | qDeleteAll(m_outputMap); | ||||
82 | m_outputMap.clear(); | ||||
83 | | ||||
84 | // Clean up | ||||
85 | if (m_queue) { | ||||
86 | delete m_queue; | ||||
87 | m_queue = nullptr; | ||||
88 | } | ||||
89 | | ||||
90 | m_connection->deleteLater(); | ||||
91 | m_connection = nullptr; | ||||
92 | | ||||
93 | WaylandInterface::handleDisconnect(); | ||||
94 | } | ||||
95 | | ||||
96 | void KWaylandInterface::setupRegistry() | ||||
97 | { | ||||
98 | m_queue = new KWayland::Client::EventQueue(this); | ||||
99 | m_queue->setup(m_connection); | ||||
100 | | ||||
101 | m_registry = new KWayland::Client::Registry(this); | ||||
102 | | ||||
103 | connect(m_registry, &KWayland::Client::Registry::outputDeviceAnnounced, | ||||
104 | this, &KWaylandInterface::addOutputDevice); | ||||
105 | | ||||
106 | connect(m_registry, &KWayland::Client::Registry::outputManagementAnnounced, | ||||
107 | this, [this](quint32 name, quint32 version) { | ||||
108 | m_outputManagement = m_registry->createOutputManagement(name, version, m_registry); | ||||
109 | } | ||||
110 | ); | ||||
111 | | ||||
112 | connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, | ||||
113 | this, [this] { | ||||
114 | m_registryInitialized = true; | ||||
115 | unblockSignals(); | ||||
116 | checkInitialized(); | ||||
117 | } | ||||
118 | ); | ||||
119 | | ||||
120 | m_registry->create(m_connection); | ||||
121 | m_registry->setEventQueue(m_queue); | ||||
122 | m_registry->setup(); | ||||
123 | } | ||||
124 | | ||||
125 | int s_outputId = 0; | ||||
126 | | ||||
127 | void KWaylandInterface::addOutputDevice(quint32 name, quint32 version) | ||||
128 | { | ||||
129 | KWaylandOutput *output = new KWaylandOutput(++s_outputId, this); | ||||
130 | output->createOutputDevice(m_registry, name, version); | ||||
131 | addOutput(output); | ||||
132 | } | ||||
133 | | ||||
134 | void KWaylandInterface::insertOutput(WaylandOutput *output) | ||||
135 | { | ||||
136 | auto *out = static_cast<KWaylandOutput*>(output); | ||||
137 | m_outputMap.insert(out->id(), out); | ||||
138 | } | ||||
139 | | ||||
140 | WaylandOutput* KWaylandInterface::takeOutput(WaylandOutput *output) | ||||
141 | { | ||||
142 | auto *out = static_cast<KWaylandOutput*>(output); | ||||
143 | return m_outputMap.take(out->id()); | ||||
144 | } | ||||
145 | | ||||
146 | void KWaylandInterface::updateConfig(KScreen::ConfigPtr &config) | ||||
147 | { | ||||
148 | config->setSupportedFeatures(Config::Feature::Writable | Config::Feature::PerOutputScaling | ||||
149 | | Config::Feature::AutoRotation | Config::Feature::TabletMode); | ||||
150 | config->setValid(m_connection->display()); | ||||
151 | | ||||
152 | //Removing removed outputs | ||||
153 | const KScreen::OutputList outputs = config->outputs(); | ||||
154 | for (const auto &output : outputs) { | ||||
155 | if (!m_outputMap.contains(output->id())) { | ||||
156 | config->removeOutput(output->id()); | ||||
157 | } | ||||
158 | } | ||||
159 | | ||||
160 | // Add KScreen::Outputs that aren't in the list yet, handle primaryOutput | ||||
161 | KScreen::OutputList kscreenOutputs = config->outputs(); | ||||
162 | for (const auto &output : m_outputMap) { | ||||
163 | KScreen::OutputPtr kscreenOutput = kscreenOutputs[output->id()]; | ||||
164 | if (!kscreenOutput) { | ||||
165 | kscreenOutput = output->toKScreenOutput(); | ||||
166 | kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); | ||||
167 | } | ||||
168 | if (kscreenOutput && m_outputMap.count() == 1) { | ||||
169 | kscreenOutput->setPrimary(true); | ||||
170 | } else if (m_outputMap.count() > 1) { | ||||
171 | // primaryScreen concept doesn't exist in Wayland, so we don't set one | ||||
172 | } | ||||
173 | output->updateKScreenOutput(kscreenOutput); | ||||
174 | } | ||||
175 | config->setOutputs(kscreenOutputs); | ||||
176 | } | ||||
177 | | ||||
178 | QMap<int, WaylandOutput*> KWaylandInterface::outputMap() const | ||||
179 | { | ||||
180 | QMap<int, WaylandOutput*> ret; | ||||
181 | | ||||
182 | QMap<int, KWaylandOutput*>::const_iterator it = m_outputMap.constBegin(); | ||||
183 | while (it != m_outputMap.constEnd()) { | ||||
184 | ret[it.key()] = it.value(); | ||||
185 | ++it; | ||||
186 | } | ||||
187 | return ret; | ||||
188 | } | ||||
189 | | ||||
190 | void KWaylandInterface::tryPendingConfig() | ||||
191 | { | ||||
192 | if (!m_kscreenPendingConfig) { | ||||
193 | return; | ||||
194 | } | ||||
195 | applyConfig(m_kscreenPendingConfig); | ||||
196 | m_kscreenPendingConfig = nullptr; | ||||
197 | } | ||||
198 | | ||||
199 | void KWaylandInterface::applyConfig(const KScreen::ConfigPtr &newConfig) | ||||
200 | { | ||||
201 | using namespace KWayland::Client; | ||||
202 | | ||||
203 | // Create a new configuration object | ||||
204 | auto wlConfig = m_outputManagement->createConfiguration(); | ||||
205 | bool changed = false; | ||||
206 | | ||||
207 | if (signalsBlocked()) { | ||||
208 | /* Last apply still pending, remember new changes and apply afterwards */ | ||||
209 | m_kscreenPendingConfig = newConfig; | ||||
210 | return; | ||||
211 | } | ||||
212 | | ||||
213 | for (const auto &output : newConfig->outputs()) { | ||||
214 | changed |= m_outputMap[output->id()]->setWlConfig(wlConfig, output); | ||||
215 | } | ||||
216 | | ||||
217 | if (!changed) { | ||||
218 | return; | ||||
219 | } | ||||
220 | | ||||
221 | // We now block changes in order to compress events while the compositor is doing its thing | ||||
222 | // once it's done or failed, we'll trigger configChanged() only once, and not per individual | ||||
223 | // property change. | ||||
224 | connect(wlConfig, &OutputConfiguration::applied, this, [this, wlConfig] { | ||||
225 | wlConfig->deleteLater(); | ||||
226 | unblockSignals(); | ||||
227 | Q_EMIT configChanged(); | ||||
228 | tryPendingConfig(); | ||||
229 | }); | ||||
230 | connect(wlConfig, &OutputConfiguration::failed, this, [this, wlConfig] { | ||||
231 | wlConfig->deleteLater(); | ||||
232 | unblockSignals(); | ||||
233 | Q_EMIT configChanged(); | ||||
234 | tryPendingConfig(); | ||||
235 | }); | ||||
236 | | ||||
237 | // Now block signals and ask the compositor to apply the changes. | ||||
238 | blockSignals(); | ||||
239 | wlConfig->apply(); | ||||
240 | } |